公司动态
过滤器(Filter)和 拦截器(Interceptor)
一、引言在开发过程中我们经常会遇到 过滤器(Filter) 和 拦截器(Interceptor)找个也是面试中容易被问道的一个问题两者的使用和作用又颇为相似是个容易被大家混淆的问题在此总结下希望能对大家有所帮助。二、Filter 过滤器介绍Filter定义Filter是sun公司中servlet2.3后增加的一个新功能在javaEE中定义了一个接口 javax.servlet.Filter来描述过滤器。Filter可以认为是Servlet的一种“加强版”它主要用于对用户请求进行预处理也可以对HttpServletResponse进行后处理是个典型的处理链。Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序.它依赖于servlet容器,在实现上基于函数回调,它可以对几乎所有请求进行过滤。Filter也可以对用户请求生成响应这一点与Servlet相同但实际上很少会使用Filter向用户请求生成响应。它是随你的web应用启动而启动的只初始化一次以后就可以拦截相关请求只有当你的web应用停止或重新部署的时候才销毁。Filter工作原理Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序其工作原理是只要你在web.xml文件配置好要拦截的客户端请求它都会帮你拦截到请求此时你就可以对请求或响应(Request、Response)统一处理等。使用Filter完整的流程是Filter对用户请求进行预处理接着将请求交给Servlet进行处理并生成响应最后Filter再对服务器响应进行后处理。当服务器启动会创建Filter对象并调用init方法只调用一次。当访问资源路径与Filter的拦截路径匹配会执行Filter中的doFilter方法当服务器关闭时会调用Filter的destroy方法来进行销毁操作。Filter应用场景在过滤器中修改字符编码CharacterEncodingFilter、在过滤器中修改HttpServletRequest的一些参数XSSFilter(自定义过滤器)如过滤低俗文字、危险字符、敏感词过滤、响应信息压缩、控制权限、控制转向、做一些业务逻辑判断等Filter代码实战public class TestFilter implements Filter { Override public void init(FilterConfig filterConfig) throws ServletException { //对filter进行一个初始化 } Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { //doFilter()前的代码是对用户请求进行预处理 System.out.println(我是Filter,执行filterChain.doFilter之前。); //执行Filter链中的下一个Filter filterChain.doFilter(request, response); //doFilter()后的代码是对用户请求进行后处理 System.out.println(我是Filter,执行filterChain.doFilter之后。); } Override public void destroy() { //对filter进行一个销毁 } }配置方式1 在web-xml 中进行配置filter description测试过滤器/description filter-nameTestFilter/filter-name filter-classcom.test.filtes.TestFilter/filter-class /filter filter-mapping filter-nameTestFilter/filter-name url-pattern/*/url-pattern /filter-mapping配置方式2通过WebFilter注解配置WebFilter(urlPatterns /test001) public class TestFilter1 implements Filter { Override public void init(javax.servlet.FilterConfig filterConfig) throws ServletException { System.out.println(TestFilter1 init); } Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //在DispatcherServlet之前执行 System.out.println(doFilter before); filterChain.doFilter(servletRequest, servletResponse); // 在视图页面返回给客户端之前执行但是执行顺序在Interceptor之后 System.out.println(doFilter after); } Override public void destroy() { System.out.println(TestFilter destroy); } } //2.在启动类添加ServletComponentScan SpringBootApplication ServletComponentScan public class TestbootApplication { public static void main(String[] args) { SpringApplication.run(TestbootApplication.class, args); } }PS: 多个filter 实现WebFilter难以控制filter 的过滤顺序与其包名和文件名有关慎用。配置方式3 通过Bean来配置//1.初始化Filter Component public class TestFilter2 implements Filter{ Override public void init(javax.servlet.FilterConfig filterConfig) throws ServletException { System.out.println(Filter2 init); } Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //在DispatcherServlet之前执行 System.out.println(doFilter2 before); filterChain.doFilter(servletRequest, servletResponse); // 在视图页面返回给客户端之前执行但是执行顺序在Interceptor之后 System.out.println(doFilter2 after); } Override public void destroy() { System.out.println(Filter2 destroy); } } Component public class TestFilter3 implements Filter{ Override public void init(javax.servlet.FilterConfig filterConfig) throws ServletException { System.out.println(Filter3 init); } Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //在DispatcherServlet之前执行 System.out.println(doFilter3 before); filterChain.doFilter(servletRequest, servletResponse); // 在视图页面返回给客户端之前执行但是执行顺序在Interceptor之后 System.out.println(doFilter3 after); } Override public void destroy() { System.out.println(Filter3 destroy); } } //2.注册到config Configuration public class FilterConfig { Bean public FilterRegistrationBean testFilter3RegistrationBean() { FilterRegistrationBean registration new FilterRegistrationBean(new TestFilter2()); registration.addUrlPatterns(/hello world); registration.setOrder(1); // 值越小越靠前此处配置有效 return registration; } Bean public FilterRegistrationBean testFilter4RegistrationBean() { FilterRegistrationBean registration new FilterRegistrationBean(new TestFilter3()); registration.addUrlPatterns(/hello world); registration.setOrder(2); return registration; } }三、Interceptor 拦截器介绍只针对spring介绍Interceptor 定义拦截器Interceptor和Servlet无关它依赖于Web框架在SpringMVC中就依赖于SpringMVC框架由SpringMVC框架实现。在Struts2中同理它是一种可以让你在Action执行之前和Result执行之后进行一些功能处理的机制。在实现上,基于Java的反射机制属于面向切面编程AOP的一种运用。可以用于在某个方法或者字段被访问之前进行拦截然后在之前或者之后加入某些统一的处理方法。就是在action的前、后、甚至抛出异常时进行处理比如动态代理就是拦截器的简单实现。拦截器将很多service或者Controller中共有的行为提炼出来在某些方法执行的前后执行提炼为通用的处理方式让被拦截的方法都能享受这一共有的功能让代码更加简洁同时当共有的功能需要发生调整、变动的时候不必修改很多的类或者方法只要修改这个拦截器就可以了可复用性很强。SpringMVC的拦截器基于HandlerInterceptor接口来实现所以只要实现HandlerInterceptor接口或者继承它的实现类。Interceptor 工作原理拦截器的实现分为两步第一步创建一个普通的拦截器实现 HandlerInterceptor 接口并重写接口中的相关方法第二步将上一步创建的拦截器加入到 Spring Boot 的配置文件中。preHandler(HttpServletRequest request, HttpServletResponse response, Object handler)方法在请求处理之前被调用。如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)方法在当前请求处理完成之后所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法需要在当前对应的 Interceptor 类的 preHandle 方法返回值为 true 时才会执行。顾名思义该方法将在整个请求结束之后也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理Interceptor 应用场景日志记录记录请求操作日志(用户ip,访问时间等)权限检查判断用户是否有权限访问资源,如校验token 日志记录性能监控记录请求-响应时间preHandle记录开始时间afterCompletion记录结束时间开始时间相减去结束时间登录验证判断用户是否登录sign校验封禁校验等处理cookie主题国际化本地化等filter可以实现的功能intercepter基本上都能实现Interceptor 代码实战配置方式1 在springmvc.xml 中进行配置public class BaseInterceptor implements HandlerInterceptor{ public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println(Before...); System.out.println(返回true继续执行,返回false则结束请求); return true; } public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println(渲染试图前,action执行后...); } public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) System.out.println(整个请求结束之后进行资源清理等); }mvc:interceptors !-- 如果直接配置bean,代表对所有请求都拦截 -- bean namebaseInterceptor classcom.test.interceptor.BaseInterceptor / mvc:interceptor !-- /**拦截所有/*拦截一层目录结构请求 -- mvc:mapping path/**/ !-- 不拦截的请求 -- mvc:exclude-mapping path/testRequestEntity/ !-- 自定义的拦截器引用 -- ref beanbaseInterceptor/ref /mvc:interceptor mvc:interceptors/配置方式2 WebConfig将拦截器注册添加到拦截器链/** * 自定义拦截器 */ Component public class MyInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 统一拦截查询token) String token request.getHeader(token); if(StringUtils.isEmpty(token)){ // return false; } System.out.println(preHandle方法在控制器的处理请求方法调用之后解析视图之前执行); return true; // 返回 true: 正常执行 false:该请求停止向下运行 } Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, Nullable ModelAndView modelAndView) throws Exception { System.out.println(postHandle方法在控制器的处理请求方法调用之后解析视图之前执行); } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Nullable Exception ex) throws Exception { System.out.println(Time Taken afterCompletion ); } }Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInterceptor()).addPathPatterns(/**); // .excludePathPatterns(/user/get)/.addPathPatterns(). 可以排除和指定设置 } Bean public MyInterceptor myInterceptor() { return new MyInterceptor(); } }四、过滤器(Filter)和拦截器(Interceptor) 、servlet、controller的执行顺序五、过滤器(Filter)和拦截器(Interceptor)区别过滤器(Filter)和拦截器(Interceptor)区别过滤器(Filter)拦截器(Interceptor)总结定义位置Filter定义在java.servlet包下接口HandlerInterceptor定义在org.springframework.web.servlet包下配置位置配置在web.xml中配置在springmvc.xml中作用位置Filter在只在 Servlet 前后起作用Filter 通常不考虑servlet 的实现拦截器能够深入到方法前后、异常抛出前后等因此拦截器的使用具有更大的弹性。允许用户介入hook into请求的生命周期在Spring构架的程序中要优先使用拦截器。几乎所有 Filter 能够做的事情 interceptor 都能够轻松的实现使用范围Filter 是 Servlet 规范规定的而拦截器既可以用于Web程序也可以用于Application、Swing程序中。遵循规范Filter 是遵循 Servlet 规范而拦截器是在 Spring容器内的是Spring框架支持的。与spring关系Filter 不能够使用 Spring 容器资源Interceptor 是被 Spring 调用Spring 中使用 interceptor 更容易调用方Filter 是被 Server(like Tomcat) 调用Interceptor 是被 Spring 调用因此 Filter 总是优先于 Interceptor 执行实现方式Filter 基于函数回掉Interceptor基于java反射