一、前言
经常做web开发的人员对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注册
publicclassFilterConfig { publicFilterRegistrationBeantestFilter3RegistrationBean() { FilterRegistrationBeanregistration=newFilterRegistrationBean(newTestFilter3()); registration.addUrlPatterns("/hello"); registration.setOrder(1); // 值越小越优先执行,此处配置有效returnregistration; } publicFilterRegistrationBeantestFilter4RegistrationBean() { FilterRegistrationBeanregistration=newFilterRegistrationBean(newTestFilter4()); registration.addUrlPatterns("/hello"); registration.setOrder(2); returnregistration; } }
3.使用@WebFilter 注解配置
在filter上增加@WebFilter注解,在启动类上增加@ServletComponentScan注解
此方式无法通过使用@Order注解来指定filter执行顺序,更详细的原因需要翻看源码。
urlPatterns="/hello") (publicclassTestFilter1implementsFilter { publicvoidinit(javax.servlet.FilterConfigfilterConfig) throwsServletException { System.out.println("##############Filter1 init##############"); } publicvoiddoFilter(ServletRequestservletRequest, ServletResponseservletResponse, FilterChainfilterChain) throwsIOException, ServletException { //在DispatcherServlet之前执行System.out.println("##############doFilter1 before##############"); filterChain.doFilter(servletRequest, servletResponse); // 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后System.out.println("##############doFilter1 after##############"); } publicvoiddestroy() { 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接口来创建拦截器
publicclassTestInterceptor1implementsHandlerInterceptor { publicbooleanpreHandle(HttpServletRequestrequest, HttpServletResponseresponse, Objecthandler) throwsException { System.out.println("##############TestInterceptor1 preHandle##############"); returntrue; } //在Controller之后的DispatcherServlet之后执行publicvoidpostHandle(HttpServletRequestrequest, HttpServletResponseresponse, Objecthandler, ModelAndViewmodelAndView) throwsException { System.out.println("##############TestInterceptor1 postHandle##############"); } // 在页面渲染完成之后返回给客户端执行publicvoidafterCompletion(HttpServletRequestrequest, HttpServletResponseresponse, Objecthandler, Exceptionex) throwsException { System.out.println("##############TestInterceptor1 afterCompletion##############"); } }
(二)使用方式
通过实现WebMvcConfigurer接口来注册拦截器
publicclassWebMvcConfigimplementsWebMvcConfigurer { privateTestInterceptor1testInterceptor1; privateTestInterceptor2testInterceptor2; publicvoidaddInterceptors(InterceptorRegistryregistry) { 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,可以调用业务逻辑。