SpringBoot(六):过滤器和拦截器

来源:互联网 发布:java nanotime 转毫秒 编辑:程序博客网 时间:2024/06/07 04:11

1.过滤器

它使用户可以改变一个request和修改一个response.

  Filter 不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理request,

也可以在response离开servlet时处理response.换种说法,filter其实是一个“servlet chaining“(servlet 链).


2.拦截器

        动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重用部分的方式。在AOP(Aspect-Oriented Programming)中拦截器用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。


3.拦截器与过滤器的区别:

过滤器可以简单理解为“取你所想取”,忽视掉那些你不想要的东西;

拦截器可以简单理解为“拒你所想拒”,关心你想要拒绝掉哪些东西,比如一个BBS论坛上拦截掉敏感词汇。
1.拦截器是基于java反射机制的,而过滤器是基于函数回调的。
2.过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
3.拦截器只对action起作用,而过滤器几乎可以对所有请求起作用。
4.拦截器可以访问action上下文、值栈里的对象,而过滤器不能。


4.过滤器在springboot中的使用:

方法一:实现Filter类,重写方法,并在类上加@Compnent注解

/** * filter 耗时 * @author mj */@Componentpublic class MyFilter implements Filter {@Overridepublic void init(FilterConfig arg0) throws ServletException {System.out.println("My filter init");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("My filter start");long start = new Date().getTime();chain.doFilter(request, response);System.out.println("My filter 耗时:"+ (new Date().getTime() - start));System.out.println("My filter finish");}@Overridepublic void destroy() {System.out.println("My filter destroy");}}
方法二:

上面代码不加@component注解,使用config注入

/** * @author mj * */@Configurationpublic class WebConfig{@Beanpublic FilterRegistrationBean myFilter() {FilterRegistrationBean registrationBean = new FilterRegistrationBean();MyFilter myFilter = new MyFilter();registrationBean.setFilter(myFilter);   //设置过滤器List<String> urls = new ArrayList<>();urls.add("/*");    //过来请求registrationBean.setUrlPatterns(urls);return registrationBean;}}

下面简单看看Filter接口的源码

/** * 过滤器是对资源(servlet或静态内容)的请求或资源的响应或两者都执行过滤任务的对象. 过滤器在doFilter()方法中执行过滤. * 每个过滤器都可以访问一个FilterConfig对象,从中可以获取其初始化参数,可以使用它可以使用的ServletContext的引用,例如,加载过滤任务所需的资源. * 过滤器在Web应用程序的部署文件中配置springmvc在web.xml中 已经为此设计确定的示例是  *   1)认证过滤器 *    2)记录和审核过滤器 *    3)图像转换滤镜 *    4)数据压缩过滤器 *    5)加密过滤器 *    6)令牌过滤器 *    7)触发资源访问事件的过滤器 *    8)XSL / T过滤器 *    9)Mime型链式过滤器 * @since Servlet 2.3 */public interface Filter {/** * 通过web容器的回掉表明filter在服务中启用 * init()方法只有在容器初始化以后调用一次 * 在过滤器被要求执行任何过滤工作之前,init方法必须成功完成. * 发生下列情况时web容器不能将filter置于服务状态。 * 1.Throws a ServletException * 2.在Web容器定义的时间段内不返回 * * @param filterConfig *           正在初始化与过滤器实例相关联的配置信息(四个方法: *               public String getFilterName();    public ServletContext getServletContext();    public String getInitParameter(String name);    public Enumeration<String> getInitParameterNames(); *           ) * * @throws ServletException *             if the initialisation fails */public void init(FilterConfig filterConfig) throws ServletException;/** * 过滤器的doFilter()方法由容器调用,每当通过链路传递请求/响应对时,由于客户端请求链路末尾的资源.  * 传递给此方法的FilterChain允许过滤器将请求和响应传递给链中的下一个实体。 * 该方法的典型实现将遵循以下模式:- * 1. 检查请求 * 2. 可选地,使用自定义实现来包装请求对象以过滤内容或头部以进行输入过滤 * 3. 可选地,使用自定义实现包装响应对象,以过滤内容或标题以进行输出过滤 * 4. a)使用FilterChain对象(chain.doFilter())调用链中的下一个实体,  * 4. b)不将请求/响应对传递给过滤器链中的下一个来阻止请求处理 * 5. 在调用过滤器链中的下一个实体后,直接在响应上设置头文件。 * @param chain *            提供访问该过滤器中的下一个过滤器以将请求和响应传递给进一步处理 */public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException;/** * 由web容器调用表明filter已经停用 * 只有过滤器的doFilter方法中的所有线程已经退出或超时时间过后,才会调用此方法。 * 在Web容器调用此方法之后,它将不再在此过滤器实例上调用doFilter方 * * 此方法为过滤器提供了清理正在保存的资源(例如,内存,文件句柄,线程)的机会,并确保任何持久状态与内存中的当前状态同步. */public void destroy();}

5.拦截器的使用

@Componentpublic class TimeInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {System.out.println("preHandle");//获取到拦截的类System.out.println(((HandlerMethod)handler).getBean().getClass().getName());//获取拦截的方法System.out.println(((HandlerMethod)handler).getMethod().getName());request.setAttribute("startTime", new Date().getTime());return true;   //返回true才会继续往下执行}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {System.out.println("postHandle");Long start = (Long) request.getAttribute("startTime");System.out.println("time interceptor 耗时:"+ (new Date().getTime() - start));}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {System.out.println("afterCompletion");Long start = (Long) request.getAttribute("startTime");System.out.println("time interceptor 耗时:"+ (new Date().getTime() - start));System.out.println("ex is "+ex);}}

@Configurationpublic class WebConfig extends WebMvcConfigurerAdapter {@Autowiredprivate TimeInterceptor timeInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(timeInterceptor);}}

源码中,DispatcherServlet的doDispatch()方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//重要代码if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}
上面指出了perHandle为false时候直接返回,如果为true才去真正的调用处理器。


HandlerInterceptor接口源码:

/** * 允许自定义处理程序执行链的工作流接口. * 应用程序可以为某些处理程序组注册任意数量的现有或自定义拦截器,以添加常见的预处理行为,而无需修改每个处理程序实现。 * HandlerInterceptor在适当的HandlerAdapter触发执行处理程序本身之前被调用.  * 该机制可用于大面积的预处理方面,例如, 用于授权检查,或常见的处理程序行为,如区域设置或主题更改。 * 其主要目的是允许分解重复的处理程序代码。 * * 在异步处理场景中, 处理程序可以在单独的线程中执行,而主线程不退出或调用{@code postHandle}和{@code afterCompletion}回调而退出。 * 当并发处理程序执行完成时,请求被发回以继续呈现模型,并且再次调用此约定的所有方法。 * * 通常每个HandlerMapping bean定义拦截器链,共享其粒度。 * 为了能够将某个拦截器链应用于一组处理程序,需要通过一个HandlerMapping bean映射所需的处理程序。 * 拦截器本身被定义为应用程序上下文中的bean,由映射bean定义通过其“interceptors”属性引用 (in XML: a <list> of <ref>). * * HandlerInterceptor基本上类似于Servlet Filter,但与后者相反,它只允许自定义预处理,可以禁止执行处理程序本身以及定制后处理。 * 过滤器更强大,例如它们允许交换链中传递的请求和响应对象.  * 请注意,过滤器在应用程序上下文中的web.xml(一个HandlerInterceptor)中配置. * * 作为基本指导原则,细粒度处理程序相关的预处理任务是HandlerInterceptor实现的候选者,特别是经过考虑的常见处理程序代码和授权检查。 * 另一方面,过滤器非常适合请求内容和查看内容处理,如多部分表单和GZIP压缩。  * 这通常显示何时需要将过滤器映射到某些内容类型(例如图像)或所有请求。 * */public interface HandlerInterceptor {/** * 拦截处理程序的执行.  * 在HandlerMapping之后调用确定一个适当的处理程序对象,但在HandlerAdapter调用处理程序之前 * DispatcherServlet处理执行链中的一个处理程序,由任意数量的拦截器组成,处理程序本身在最后。 * 通过这种方法,每个拦截器可以决定中止执行链,通常发送HTTP错误或编写自定义响应。 * NOTE: * 特殊考虑适用于异步请求处理。更多细节看{@link org.springframework.web.servlet.AsyncHandlerInterceptor}. */boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;/** * 拦截处理程序的执行. * 在HandlerAdapter之后调用了调用处理程序,但在DispatcherServlet呈现视图之前。 * 可以通过给定的ModelAndView将附加的模型对象添加到视图。 * DispatcherServlet处理执行链中的一个处理程序,由任意数量的拦截器组成,处理程序本身在最后。 * 使用这种方法,每个拦截器都可以对执行进行后处理,按照执行链的相反顺序进行应用。 */void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;/** * 完成请求处理后的回调,即渲染后的视图。 将调用处理程序执行的任何结果,从而允许正确的资源清理。 * 只有preHandle()方法成功完成并返回true才执行 * 与postHandle()方法一样,将以相反的顺序在链中的每个拦截器上调用该方法,因此第一个拦截器将是最后一个被调用的拦截器. * @param ex 处理程序执行时抛出异常(如果有的话) */void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;}