上一篇博客中我们探讨了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发出的请求
此处我们可以发现,
- 当同时存在两个过滤器时,如果请求只符合其中一个过滤器的规则,则只会调用该过滤器
- 当两个过滤器被同时调用的时候,则会组成过滤器链进行嵌套,如下图
二、相同过滤规则的过滤器的初始化顺序
我们以以下两个完全相同的过滤器为例
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类在调用时初始化