前言
我们学习Servlet的时候,过滤器Filter是绕不过,而且较为重要的一个知识点。Filter随web应用启动而启动的,只初始化一次,以后就可以拦截相关的请求,只有当你的web应用停止或重新部署的时候才能销毁。在实际项目中,我们常常使用过滤器来对过滤字符编码或者做一些业务逻辑判断。
这篇文章,将对如何应用Filter,并且对过滤器的执行流程进行讲解,希望对过滤器不了解的学习者一个参考。
一、什么是过滤器
过滤器 Filter,是在 Servlet 规范中定义的,是 Servlet 容器支持的,该接口定义在 javax.servlet包下,主要是在客户端请求(HttpServletRequest)进行预处理,以及对服务器响应(HttpServletResponse)进行后处理。
我们要自定义过滤器,需要实现Filter 接口。
该接口一共有三个方法
- init 方法,在自定义Filter初始化的时候调用
- doFilter,每次匹配到用户请求,都会先经过这个方法,执行doFilter放行后,客户端的请求才能到servlet上。
- destroy 自定义Filter被销毁的时候调用
二、自定义过滤器
定义过滤器有两种方式,第一种可以通过注解定义,第二种需要在创建过滤器后,在web.xml文件上配置过滤器。为了更好地对过滤器的拦截顺序进行讲解,这里的话会创建两个过滤器。
package com.xiaoming.util;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
@Order(1)
@WebFilter(urlPatterns = "/user/*",filterName = "filter1")
public class MyFilter1 implements Filter {
// 在Filter初始化的时候调用
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter1 has been initialized ...");
}
/** 每个用户请求都会调用到这个方法,校验通过则doFilter放行到下一个过滤器
* 等到请求通过所有过滤链上的校验后,才能到达servlet
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter1开始执行,对"+((HttpServletRequest)servletRequest).getRequestURL().toString()+" 进行过滤 ");
System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
System.out.println("doFilter1执行结束");
}
// Filter对象被销毁的时候调用,注意,执行该方法后会在调用一次dofilter
public void destroy() {
System.out.println("MyFilter1 has been destroyed...");
}
}
过滤器1
package com.xiaoming.util;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
@Order(2)
@WebFilter(filterName = "filter2",urlPatterns = "/user/*")
public class MyFilter2 implements Filter {
// 在Filter初始化的时候调用
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter2 has been initialized ...");
}
/** 每个用户请求都会调用到这个方法,校验通过则doFilter放行到下一个过滤器
* 等到请求通过所有过滤链上的校验后,才能到达servlet
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter2开始执行,对"+((HttpServletRequest)servletRequest).getRequestURL().toString()+" 进行过滤 ");
System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
System.out.println("doFilter2执行结束");
}
// Filter对象被销毁的时候调用,注意,执行该方法后会在调用一次dofilter
public void destroy() {
System.out.println("MyFilter2 has been destroyed...");
}
}
过滤器2
这里对使用到的注解进行一个简单的介绍
- @Component 让spring接管这个bean
- @Order 执行顺序,数字越小,越早执行
- @WebFilter
- filterName 过滤器的名称
- urlPatterns 过滤的url规则
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/save",method = RequestMethod.GET)
public String save(){
System.out.println("user save ...");
return "success";
}
}
将项目部署到tomcat后,访问http://localhost:8088/user/save,得到结果如下
我们可以发现,过滤器的执行顺序是
过滤器1.doFilter() -> 过滤器2.doFilter() -> controller方法调用 -> 过滤器2.doFilter()结束 -> 过滤器2.doFilter()
结束。我们可以用下图来更好的理解这个过程Servlet在Filter的里层,所有到servlet的请求都必须先经过过滤器Filter,而过滤器往往有多个,组成了过滤链,Filter执行doFilter()的话就将请求放行到下一个过滤器。
本案例的话,Filter1比过滤器的级别要高,调用顺序在Filter2之前,所以请求过来的时候,会先经过Filter1,再经过Filter2,请求结束后,根据堆栈的执行顺序,先执行Filter2.doFilter后面的代码,再执行Filter1剩余的代码。
另外,如果是采用配置文件的方式配置过滤器的话,需要在web.xml文件进行如下配置
<filter>
<filter-name>filter2</filter-name>
<filter-class>com.xiaoming.util.MyFilter2</filter-class>
</filter>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>/user/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>filter1</filter-name>
<filter-class>com.xiaoming.util.MyFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/user/*</url-pattern>
</filter-mapping>
过滤器的执行顺序依照配置文件中定义的过滤器顺序来执行
参考文章:
https://zhuanlan.zhihu.com/p/69060111
https://zhuanlan.zhihu.com/p/161740475