一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。
一、前言
filter和Interceptor都可以拦截请求,对参数进行过滤,安全校验、记录日志等工作,但是两者还是有本质的区别。
二、过滤器
过滤器( Filter),是 JavaEE 的标准,依赖于 Servlet 容器,配置在web.xml 文件中,可以配置多个,执行的顺序是根据配置顺序从上到下。
过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是设置字符集、控制权限、控制转向、做一些业务逻辑判断等。其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应(Request、Response)统一配置请求编码以及过滤一些非法参数,垃圾信息,简化操作;同时还可进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等等工作。它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。
Filter可以认为是Servlet的一种“加强版”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
(一)过滤器的作用
- 在请求到达serverlt之前拦截客户的HttpServletRequest,获取、修改请求数据
- 在响应返回客户端之前拦截HttpServletResponse,检查、修改响应数据。
(二)使用场景
- 负责检查用户请求,根据请求过滤用户非法请求,springsecurity就是通过filter实现
- 日志Filter:详细记录某些特殊的用户请求
- 负责解码的Filter:包括对非标准编码的请求解码。
(三)创建方式
- 实现javax.servlet.Filter接口
- 继承spring中OncePerRequestFilter类
(四)使用方式
1.在xml中配置
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.使用FilterRegistrationBean注册
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean testFilter3RegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter3());
registration.addUrlPatterns("/hello");
registration.setOrder(1); // 值越小越优先执行,此处配置有效
return registration;
}
@Bean
public FilterRegistrationBean testFilter4RegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter4());
registration.addUrlPatterns("/hello");
registration.setOrder(2);
return registration;
}
}
3.使用@WebFilter 注解配置
在filter上增加@WebFilter注解,在启动类上增加@ServletComponentScan注解
此方式无法通过使用@Order注解来指定filter执行顺序,更详细的原因需要翻看源码。
@WebFilter(urlPatterns = "/hello")
public class TestFilter1 implements Filter {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
System.out.println("##############Filter1 init##############");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//在DispatcherServlet之前执行
System.out.println("##############doFilter1 before##############");
filterChain.doFilter(servletRequest, servletResponse);
// 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
System.out.println("##############doFilter1 after##############");
}
@Override
public void destroy() {
System.out.println("##############Filter1 destroy##############");
}
}
三、拦截器
拦截器是SpringMVC中实现的一种基于Java反射(动态代理)机制的方法增强工具,拦截器的实现是继承HandlerInterceptor 接口,并实现接口的preHandle、postHandle和afterCompletion方法。
1、preHandle:请求方法前置拦截,该方法会在Controller处理之前进行调用,Spring中可以有多个Interceptor,这些拦截器会按照设定的Order顺序调用,当有一个拦截器在preHandle中返回false的时候,请求就会终止。
2、postHandle:preHandle返回结果为true时,在Controller方法执行之后,视图渲染之前被调用
3、afterCompletion:在preHandle返回ture,并且整个请求结束之后,执行该方法。
(一)创建方式
通过实现HandlerInterceptor接口来创建拦截器
@Component
public class TestInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("##############TestInterceptor1 preHandle##############");
return true;
}
//在Controller之后的DispatcherServlet之后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("##############TestInterceptor1 postHandle##############");
}
// 在页面渲染完成之后返回给客户端执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("##############TestInterceptor1 afterCompletion##############");
}
}
(二)使用方式
通过实现WebMvcConfigurer接口来注册拦截器
@Configurer
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private TestInterceptor1 testInterceptor1;
@Autowired
private TestInterceptor2 testInterceptor2;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(testInterceptor1)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
.addPathPatterns("/hello");
registry.addInterceptor(testInterceptor2).addPathPatterns("/hello");
}
}
四、总结:
- Filter需要在web.xml中配置,依赖于Servlet;
- Interceptor需要在SpringMVC中配置,依赖于框架;
- 两者的本质区别:拦截器(Interceptor)是基于Java的反射机制,而过滤器(Filter)是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。
- 拦截器可以获取IOC容器中的各个bean,而过滤器不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。