文章目录
- 【README】
- 【1】springmvc拦截器回顾
- 【1.1】定义与应用
- 【1.2】拦截器作用范围
- 【2】servlet过滤器回顾
- 【2.1】过滤器定义与应用
- 【2.2】过滤器作用范围
- 【3】springmvc拦截器与servlet过滤器区别(重要*)
- 【3.1】拦截方法调用代码实现
【README】
代码详情参见: springmvcDiscoverFirstDemo【github】
1)本文根据spring揭秘25-springmvc03-其他组件(文件上传+拦截器+处理器适配器+异常统一处理) 中的拦截器与过滤器进行总结;
- 确切的说本文讨论的拦截器指的是springmvc拦截器,过滤器是servlet过滤器;springmvc是基于servlet构建的web框架,但两者有区别;(当然了,servlet有拦截器,也有过滤器)
2)拦截器与过滤器都可以对业务逻辑做拦截,即在上下文织入逻辑,所以把两者放在一起比较;
- 拦截器与过滤器最重要的应用场景:本文认为是偷梁换柱, 在拦截方法中,用新对象替换已有对象 ; (当然也有其他场景,如日志收集, 参数校验)
【1】springmvc拦截器回顾
【1.1】定义与应用
1)springmvc拦截器定义:基于servlet拦截器思想,springmvc提供的对servlet内部处理逻辑进行拦截的抽象接口;
2)应用场景:日志收集, 参数校验等; 其实最重要的应用,应该是偷梁换柱;就是在拦截方法中,可以用新对象替换已有对象 ;
【HandlerInterceptor】
package org.springframework.web.servlet;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;public interface HandlerInterceptor {default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {}default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {}
}
【TimeCostHandlerInterceptor】自定义拦截器
public class TimeCostHandlerInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {request.setAttribute("startTime" , System.currentTimeMillis());return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {Long startTime = (Long) request.getAttribute("startTime");System.out.println(request.getServletPath() + " 执行耗时统计(单位毫秒)=" + (System.currentTimeMillis() - startTime));}
}
【dispatcher-servlet.xml】装配拦截器到HandlerMapping
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 注册自定义处理器拦截器 --><bean id="timeCostHandlerInterceptor" class="com.tom.springmvc.handlerinterceptor.TimeCostHandlerInterceptor"/><!-- 注册HandllerMapping bean到springweb容器, BeanNameUrlHandlerMapping使用URL与Controller的bean名称进行匹配 --><bean id="beanNameUrlHandlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"><property name="interceptors"><list><ref bean="timeCostHandlerInterceptor" /> <!-- 装配拦截器 --> </list></property></bean><!-- SimpleUrlHandlerMapping: 可以配置web请求到具体二级控制器的映射, 可以把一组或多组拥有相似特征的web请求映射给二级控制器--><bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><!-- 设置SimpleUrlHandlerMapping优先级为1,优先匹配,若有多个HandlerMapping时 --><property name="order" value="1" /><property name="mappings"><value>/userController.do=userController/bankCard*.do=bankCardController/bankCard/*.do=bankCardController/pdfUrlView*.do=pdfUrlViewController</value></property></bean></beans>
【1.2】拦截器作用范围
1) 由配置可知,拦截器的作用范围是HandlerMapping ; 如本文配置了2个HandlerMapping,包括 BeanNameUrlHandlerMapping, SimpleUrlHandlerMapping ;而只有BeanNameUrlHandlerMapping装配了timeCostHandlerInterceptor拦截器,而SimpleUrlHandlerMapping 没有;
- 所以:通过BeanNameUrlHandlerMapping找到的二级处理器,并调用该处理器时,才会有timeCostHandlerInterceptor拦截功能;而SimpleUrlHandlerMapping 没有;
- 而二级处理器是由一级处理器DispatcherServlet调用HandlerMapping查找到的,所以总结起来,springmvc拦截器是在Servlet内部做拦截;(补充:servlet拦击器是对servlet外部做拦截,即对servlet上下文做拦截) ;
【2】servlet过滤器回顾
【2.1】过滤器定义与应用
1)servlet过滤器:servlet框架提供的对servlet逻辑做前置过滤的抽象接口;
2)应用场景:如参数校验; 与拦截器类似, 其实最重要的应用,应该是偷梁换柱;就是在拦截方法(doFilter)中,可以用新对象替换已有对象 ;
package jakarta.servlet;import java.io.IOException;public interface Filter {default public void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException;default public void destroy() {}
}
【CustomFilter】自定义过滤器
public class CustomFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println(request.getServletContext().getContextPath() + " CustomFilter 过滤器执行");chain.doFilter(request, response);System.out.println(request.getServletContext().getContextPath() + " CustomFilter 过滤器执行完成后");}
}
【web.xml】注册过滤器代理到servlet容器
<!-- 注册过滤器代理 -->
<filter><filter-name>customFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping><filter-name>customFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
【applicationContext.xml】自定义过滤器注册到spring容器
<!-- 注册自定义过滤器 -->
<bean id="customFilter" class="com.tom.springmvc.filter.CustomFilter"/>
【2.2】过滤器作用范围
1)过滤器: 对servlet进行拦截,且仅可以做前置拦截(在servlet入口方法执行前拦截); 若满足条件,则调用FilterChain.doFilter()放行;否则封装响应报文,直接返回;
【3】springmvc拦截器与servlet过滤器区别(重要*)
1)拦截位置不同:
- 拦截器有3个拦截方法,包括preHandle, postHandle, afterCompletion 方法;可以在上文与下文以及结束时拦截;
- 过滤器只有1个拦截方法, 包括doFilter方法; 也可以在上下文拦截(FilterChain.doFilter()的上文和下文织入业务逻辑);
2)放行请求的方式不同:
- 拦截器的preHandle方法通过返回true/false放行请求;
- 过滤器的doFilter()) 通过调用 FilterChain.doFilter() 放行请求; (责任链模式)
3)作用范围不同:
- 拦截器是装配给HandlerMapping,是对servlet内部逻辑进行拦截;(本文特指springmvc拦截器)
- 过滤器是对servlet上文进行拦截,是对servlet外部逻辑进行拦截;(本文特指servlet过滤器)
4)底层拦截机制不同:
- 拦截器:若有多个拦截器,只要拦截器的preHandle执行通过(返回true,这个非常重要),则其afterCompletion 一定执行(无论是否抛出异常);类似于双向链表;
- 参考【3.1】HandlerExecutionChain#applyPreHandle()代码, 只有preHandle返回true的拦截器,才会执行其afterCompletion 方法;
- 过滤器:若有多个过滤器,是通过 FilterChain.doFilter() 把请求透传下去,类似于单向链表;
【3.1】拦截方法调用代码实现
【HandlerExecutionChain】 拦截方法调用
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i = 0; i < this.interceptorList.size(); i++) {HandlerInterceptor interceptor = this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {// 若有一个拦击器前置拦截返回false,则已执行preHandle方法的所有拦截器的afterCompletion方法被执行 【因为暂存了已执行preHandle方法的拦截器索引或下标】 triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i; // 暂存已执行preHandle方法的拦截器索引 (下标递增)}return true;
}/*** Apply postHandle methods of registered interceptors.*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {for (int i = this.interceptorList.size() - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);interceptor.postHandle(request, response, this.handler, mv);}
}/*** Trigger afterCompletion callbacks on the mapped HandlerInterceptors.* Will just invoke afterCompletion for all interceptors whose preHandle invocation* has successfully completed and returned true.*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {// 把已执行preHandle方法的拦截器索引取出,执行其afterCompletion方法(下标递减)for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i);try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}
}