Java中Servlet类的过滤器Filter的加载顺序

上一篇博客中我们探讨了servlet类的生命周期和加载问题,现在我们来探讨一下Filter的初始化加载顺序问题

一、多个过滤器的加载顺序

首先我们以一个登陆界面servlet类和两个过滤器(一个编码过滤器,一个登陆判断过滤器为例)
servlet类

@WebServlet(value = "/loginServlet",name = "LoginServlet",loadOnStartup = 1,asyncSupported = true)
public class LoginServlet extends HttpServlet {

    @Override
    public void destroy() {
        super.destroy();
        System.out.println("LoginServlet销毁");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("LoginServlet初始化");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession httpSession = req.getSession();

        //设置编码规则
        req.setCharacterEncoding("utf-8");
        //获取用户名
        String uname = req.getParameter("uname");
        if ("admin".equals(uname)){
            httpSession.setAttribute("user","uname");
            resp.sendRedirect("person.jsp");//重定向到个人主页

        }else{
            httpSession.setAttribute("errMsg","登陆失败,请重试");
            resp.sendRedirect("login.jsp");//重定向回登陆页面
        }
    }
}

编码过滤器

public class EncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("EncodingFilter初始化");
    }

    /**
     * 设置过滤规则,和设置请求下一步的操作(要怎么才能符合,符合了之后要怎么做)
     * @param servletRequest
     * 是HttpServletRequest(相当于Jsp里面的Request)的父类
     * @param servletResponse
     * 是HttpServletResponse(相当于Jsp里面的Response)的父类
     * @param filterChain
     * 用于控制请求的下一步操作,同时将最新的Request和Response对象携带过去
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("EncodingFilter接受");
        //设置过滤规则为编码处理
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        //指定请求的下一步操作(放过该请求)
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("EncodingFilter响应");
    }

    @Override
    public void destroy() {
        System.out.println("EncodingFilter销毁");
    }
}

登陆过滤器

public class PermissionFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
       System.out.println("PermissionFilter初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("PermissionFilter接受");

        //获取用户的登陆标识
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        HttpSession session= request.getSession();
        Object object = session.getAttribute("user");
        if (object ==null){//未登录
            response.sendRedirect("/login.jsp");//拦截之后,会默认找当前目录下的这个jsp,但是被拦截的person.jsp在permission吗,目录里,所以此处的login.jsp要指定一下目录

        }else{
            filterChain.doFilter(servletRequest,servletResponse);

        }
        System.out.println("PermissionFilter放过");
    }

    @Override
    public void destroy() {
         System.out.println("PermissionFilter销毁");
    }
}

登陆前端页面

<form action="loginServlet">
    <input type="text" name="uname">
    <input type="submit" value="login">
</form>

我们在通过实现写好的登陆页面进行登陆后结果如下
在这里插入图片描述
注意:此处第一次EncodingFilter执行是LoginServlet类被login.jsp请求时使用的,第二次则是LoginServlet类重定向后login.jsp发出的请求

此处我们可以发现,

  1. 当同时存在两个过滤器时,如果请求只符合其中一个过滤器的规则,则只会调用该过滤器
  2. 当两个过滤器被同时调用的时候,则会组成过滤器链进行嵌套,如下图
    在这里插入图片描述
二、相同过滤规则的过滤器的初始化顺序

我们以以下两个完全相同的过滤器为例

public class LCFilter1 implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("LCFilter1初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("LCFilter1 doFilter 接收");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("LCFilter1 doFilter 放过");
    }

    @Override
    public void destroy() {
        System.out.println("LCFilter1销毁");
    }
}

public class LCFilter2 implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("LCFilter2初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("LCFilter2 doFilter 接收");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("LCFilter2 doFilter 放过");
    }

    @Override
    public void destroy() {
        System.out.println("LCFilter2销毁");
    }
}

我们让他们同时对loginServlet类进行过滤

<filter>
    <filter-name>LCFilter1</filter-name>
    <filter-class>cn.yunhe.filter.LCFilter1</filter-class>
</filter>

<filter-mapping>
    <filter-name>LCFilter1</filter-name>
    <url-pattern>/loginServlet</url-pattern>
</filter-mapping>

<filter>
    <filter-name>LCFilter2</filter-name>
    <filter-class>cn.yunhe.filter.LCFilter2</filter-class>
</filter>

<filter-mapping>
    <filter-name>LCFilter2</filter-name>
    <url-pattern>/loginServlet</url-pattern>
</filter-mapping>

结果如下

  • 初始化顺序
    在这里插入图片描述
  • 调用顺序
    在这里插入图片描述

首先,这两个过滤器也是符合过滤器链原则的,会安装嵌套顺序进行加载
但是他们之间没有逻辑顺序,执行顺序是如何决定的呢?

我们将XML文件中的过滤器配置顺序颠倒之后发现

在这里插入图片描述
得出结论,相同过滤规则过滤器的执行顺序和配置文件的配置顺序相同,先配置的先执行

这一结论也可以推到上一个例子中,颠倒配置顺序后
在这里插入图片描述

三、过滤器和servlet文件的初始化顺序

过滤器会在servlet容器被初始化之后就初始化,而servlet文件则是不调用不初始化。如果给servlet类设置上loadOnStartup属性,servlet类会和过滤器在同一阶段初始化,但是会在过滤器初始化完毕之后



loadOnStartup属性时
在这里插入图片描述
loadOnStartup属性时
过滤器在servlet容器初始化后初始化
在这里插入图片描述
servlet类在调用时初始化
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值