该系列文档是本人在学习 Spring MVC 的源码过程当中总结下来的,可能对读者不太友好,请结合个人源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读html
Spring 版本:5.2.4.RELEASEjava
该系列其余文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》git
HandlerMapping 组件,请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain
处理器执行链,包含处理器(handler
)和拦截器们(interceptors
)github
handler
处理器是 Object 类型,能够将其理解成 HandlerMethod 对象(例如咱们使用最多的 @RequestMapping
注解所标注的方法会解析成该对象),包含了方法的全部信息,经过该对象可以执行该方法web
HandlerInterceptor
拦截器对处理请求进行加强处理,可用于在执行方法前、成功执行方法后、处理完成后进行一些逻辑处理spring
因为 HandlerMapping 组件涉及到的内容比较多,考虑到内容的排版,因此将这部份内容拆分红了四个模块,依次进行分析:数组
在上一篇《HandlerMapping 组件(一)之 AbstractHandlerMapping》文档中分析了 HandlerMapping 组件的 AbstractHandlerMapping 抽象类,在获取HandlerExecutionChain
处理器执行链时,会去寻找匹配的 HandlerInterceptor 拦截器们,并添加到其中。那么本文将分享 Spring MVC 的拦截器相关内容mvc
org.springframework.web.servlet.HandlerInterceptor
,处理器拦截器接口,代码以下:app
public interface HandlerInterceptor { /** * 前置处理,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行以前 */ default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } /** * 后置处理,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行成功以后 */ default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } /** * 完成处理,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行以后(不管成功仍是失败) * 条件:执行 {@link #preHandle(HttpServletRequest, HttpServletResponse, Object)} 成功的拦截器才会执行该方法 */ default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
org.springframework.web.servlet.HandlerExecutionChain
,处理器执行链,也就是经过 HandlerMapping 组件为请求找到的处理对象,包含处理器(handler
)和拦截器们(interceptors
)cors
public class HandlerExecutionChain { /** * 处理器 */ private final Object handler; /** * 拦截器数组 */ @Nullable private HandlerInterceptor[] interceptors; /** * 拦截器数组。 * * 在实际使用时,会调用 {@link #getInterceptors()} 方法,初始化到 {@link #interceptors} 中 */ @Nullable private List<HandlerInterceptor> interceptorList; /** * 已成功执行 {@link HandlerInterceptor#preHandle(HttpServletRequest, HttpServletResponse, Object)} 的位置 * * 在 {@link #applyPostHandle} 和 {@link #triggerAfterCompletion} 方法中须要用到,用于倒序执行拦截器的方法 */ private int interceptorIndex = -1; public HandlerExecutionChain(Object handler) { this(handler, (HandlerInterceptor[]) null); } public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) { if (handler instanceof HandlerExecutionChain) { HandlerExecutionChain originalChain = (HandlerExecutionChain) handler; this.handler = originalChain.getHandler(); this.interceptorList = new ArrayList<>(); // 将原始的 HandlerExecutionChain 的 interceptors 复制到 this.interceptorList 中 CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList); // 将入参的 interceptors 合并到 this.interceptorList 中 CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList); } else { this.handler = handler; this.interceptors = interceptors; } } }
handler
:请求对应的处理器对象,能够先理解为 HandlerMethod
对象(例如咱们经常使用的 @RequestMapping
注解对应的方法会解析成该对象),也就是咱们的某个 Method 的全部信息,能够被执行interceptors
:请求匹配的拦截器数组interceptorList
:请求匹配的拦截器集合,至于为何要该属性,我还没看明白😈interceptorIndex
:记录已成功执行前置处理的拦截器位置,由于已完成处理只会执行前置处理成功的拦截器,且倒序执行addInterceptor(HandlerInterceptor interceptor)
方法,添加拦截器到 interceptorList
集合中,方法以下:
public void addInterceptor(HandlerInterceptor interceptor) { initInterceptorList().add(interceptor); } private List<HandlerInterceptor> initInterceptorList() { // 若是 interceptorList 为空,则初始化为 ArrayList if (this.interceptorList == null) { this.interceptorList = new ArrayList<>(); // 若是 interceptors 非空,则添加到 interceptorList 中 if (this.interceptors != null) { // An interceptor array specified through the constructor CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList); } } // 置空 interceptors this.interceptors = null; // 返回 interceptorList return this.interceptorList; }
getInterceptors()
方法,得到 interceptors
数组,方法以下:
@Nullable public HandlerInterceptor[] getInterceptors() { // 将 interceptorList 初始化到 interceptors 中 if (this.interceptors == null && this.interceptorList != null) { this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]); } // 返回 interceptors 数组 return this.interceptors; }
applyPreHandle(HttpServletRequest request, HttpServletResponse response)
方法,执行请求匹配的拦截器的前置处理,方法以下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { // <1> 得到拦截器数组 HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { // <2> 遍历拦截器数组 for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; // <3> 前置处理 if (!interceptor.preHandle(request, response, this.handler)) { // <3.1> 已完成处理 拦截器 triggerAfterCompletion(request, response, null); // 返回 false ,前置处理失败 return false; } // <3.2> 标记 interceptorIndex 位置 this.interceptorIndex = i; } } // <4> 返回 true ,前置处理成功 return true; }
得到拦截器数组,经过上面的 getInterceptors()
方法,得到 interceptors
数组
遍历 interceptors
拦截器数组
依次执行拦截器的前置处理
triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
方法,触发拦截器们的已完成处理,最后返回 false
interceptorIndex
属性中,为了已完成处理只会执行前置处理成功的拦截器,且倒序执行返回 true
,拦截器们的前置处理都成功
applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
方法,执行请求匹配的拦截器的后置处理,方法以下:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { // 得到拦截器数组 HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { // 遍历拦截器数组 for (int i = interceptors.length - 1; i >= 0; i--) { // 倒序 HandlerInterceptor interceptor = interceptors[i]; // 后置处理 interceptor.postHandle(request, response, this.handler, mv); } } }
triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
方法,执行请求匹配的拦截器的已完成处理,方法以下:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { // 得到拦截器数组 HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { // 遍历拦截器数组 for (int i = this.interceptorIndex; i >= 0; i--) { // 倒序!!! HandlerInterceptor interceptor = interceptors[i]; try { // 已完成处理 拦截器 interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { // 注意,若是执行失败,仅仅会打印错误日志,不会结束循环 logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } }
interceptorIndex
属性,只会执行前置处理成功的拦截器们,由于该属性定义了成功执行前置处理的拦截器的位置HandlerMapping 接口体系的结构以下:
能够看到它的实现类有许多,这里来看几个重要的类
org.springframework.web.servlet.handler.MappedInterceptor
,实现 HandlerInterceptor 接口,支持地址匹配的 HandlerInterceptor 实现类
每个 <mvc:interceptor />
标签,将被解析成一个 MappedInterceptor 类型的 Bean 拦截器对象
public final class MappedInterceptor implements HandlerInterceptor { /** * 匹配的路径 */ @Nullable private final String[] includePatterns; /** * 不匹配的路径 */ @Nullable private final String[] excludePatterns; /** * 拦截器对象 */ private final HandlerInterceptor interceptor; /** * 路径匹配器 */ @Nullable private PathMatcher pathMatcher; public MappedInterceptor(@Nullable String[] includePatterns, HandlerInterceptor interceptor) { this(includePatterns, null, interceptor); } public MappedInterceptor(@Nullable String[] includePatterns, @Nullable String[] excludePatterns, HandlerInterceptor interceptor) { this.includePatterns = includePatterns; this.excludePatterns = excludePatterns; this.interceptor = interceptor; } public MappedInterceptor(@Nullable String[] includePatterns, WebRequestInterceptor interceptor) { this(includePatterns, null, interceptor); } }
includePatterns
:拦截器须要匹配的请求路径excludePatterns
:拦截器须要排除的请求路径pathMatcher
:路径匹配器interceptor
:拦截器对象经过前面三个属性去判断请求是否匹配
matches(String lookupPath, PathMatcher pathMatcher)
方法,判断请求路径是否匹配,方法以下:
public boolean matches(String lookupPath, PathMatcher pathMatcher) { PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher); // <1> 先判断该路径是否在不匹配的路径中 if (!ObjectUtils.isEmpty(this.excludePatterns)) { for (String pattern : this.excludePatterns) { if (pathMatcherToUse.match(pattern, lookupPath)) { return false; } } } // <2> 若是匹配的路径为空,则都匹配经过 if (ObjectUtils.isEmpty(this.includePatterns)) { return true; } // <3> 判断路径是否在须要匹配的路径中 for (String pattern : this.includePatterns) { if (pathMatcherToUse.match(pattern, lookupPath)) { return true; } } return false; }
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return this.interceptor.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { this.interceptor.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { this.interceptor.afterCompletion(request, response, handler, ex); }
都是直接调用interceptor
拦截器对应的方法
<mvc:interceptors>
标签<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <mvc:exclude-mapping path="/error/**" /> <bean class="com.fullmoon.study.interceptor.JwtInterceptor" /> </mvc:interceptor> </mvc:interceptors>
每个 <mvc:interceptor />
标签,将被解析成一个 MappedInterceptor
类型的 Bean 拦截器对象
而后 MappedInterceptor
类型的拦截器在 AbstractHandlerMapping 的 initApplicationContext() -> detectMappedInterceptors
会被扫描到
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { // 扫描已注册的 MappedInterceptor 的 Bean 们,添加到 mappedInterceptors 中 // MappedInterceptor 会根据请求路径作匹配,是否进行拦截 mappedInterceptors.addAll(BeanFactoryUtils .beansOfTypeIncludingAncestors(obtainApplicationContext(), MappedInterceptor.class, true, false) .values()); }
也就是说在初始化 HandlerMapping 组件的时候会扫描到咱们自定义的拦截器,并添加到属性中
<mvc:interceptor />
标签如何被解析成MappedInterceptor
对象的?
能够来看到spring-webmvc
工程的 spring.handlers
文件,以下:
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
指定了 NamespaceHandler 为 MvcNamespaceHandler
对象,也就是说<mvc />
标签会被该对象进行解析,以下:
public class MvcNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser()); } }
其中<mvc:interceptor />
标签则会被 InterceptorsBeanDefinitionParser
对象进行解析,以下:
class InterceptorsBeanDefinitionParser implements BeanDefinitionParser { @Override @Nullable public BeanDefinition parse(Element element, ParserContext context) { context.pushContainingComponent( new CompositeComponentDefinition(element.getTagName(), context.extractSource(element))); RuntimeBeanReference pathMatcherRef = null; if (element.hasAttribute("path-matcher")) { pathMatcherRef = new RuntimeBeanReference(element.getAttribute("path-matcher")); } List<Element> interceptors = DomUtils.getChildElementsByTagName(element, "bean", "ref", "interceptor"); for (Element interceptor : interceptors) { // 将 <mvc:interceptor /> 标签解析 MappedInterceptor 对象 RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class); mappedInterceptorDef.setSource(context.extractSource(interceptor)); mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); ManagedList<String> includePatterns = null; ManagedList<String> excludePatterns = null; Object interceptorBean; if ("interceptor".equals(interceptor.getLocalName())) { includePatterns = getIncludePatterns(interceptor, "mapping"); excludePatterns = getIncludePatterns(interceptor, "exclude-mapping"); Element beanElem = DomUtils.getChildElementsByTagName(interceptor, "bean", "ref").get(0); interceptorBean = context.getDelegate().parsePropertySubElement(beanElem, null); } else { interceptorBean = context.getDelegate().parsePropertySubElement(interceptor, null); } mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, includePatterns); mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, excludePatterns); mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(2, interceptorBean); if (pathMatcherRef != null) { mappedInterceptorDef.getPropertyValues().add("pathMatcher", pathMatcherRef); } String beanName = context.getReaderContext().registerWithGeneratedName(mappedInterceptorDef); context.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, beanName)); } context.popAndRegisterContainingComponent(); return null; } private ManagedList<String> getIncludePatterns(Element interceptor, String elementName) { List<Element> paths = DomUtils.getChildElementsByTagName(interceptor, elementName); ManagedList<String> patterns = new ManagedList<>(paths.size()); for (Element path : paths) { patterns.add(path.getAttribute("path")); } return patterns; } }
逻辑不复杂,会将 <mvc:interceptor />
标签解析 BeanDefinition 对象,beanClass 为 MappedInterceptor
,解析出来的属性也会添加至其中,也就会给初始化成 MappedInterceptor
类型的 Spring Bean 到 Spring 上下文中
在 SpringBoot 2.0+ 项目中,添加拦截器的方式能够以下:
@Component public class JwtInterceptor implements HandlerInterceptor { /** * 前置处理 * * @param handler 拦截的目标,处理器 * @return 该请求是否继续往下执行 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // JWT 校验 // 验证经过,返回 true,不然返回false return true; } /** 后置处理 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } /** 已完成处理 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } } @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { List<String> excludePath = new ArrayList<>(); // 将拦截器添加至 InterceptorRegistry registry.addInterceptor(jwtInterceptor()).addPathPatterns("/**").excludePathPatterns(excludePath); } @Bean public JwtInterceptor jwtInterceptor() { return new JwtInterceptor(); } }
patterns
路径没有设置好,可能在请求过程当中发生的错误会被拦截器拦截到,能够在拦截器中根据自定义注解进行拦截处理由于 JwtInterceptor
不是 MappedInterceptor
类型的拦截器,不会被 AbstractHandlerMapping 探测到,既然这样子,那么咱们就直接调用 AbstractHandlerMapping 的 setInterceptors(Object... interceptors)
设置进去不就行了
因为 Spring 5.0 废弃了 WebMvcConfigurerAdapter,因此须要经过 WebMvcConfigurer 接口来添加咱们的拦截器,那么在 Spring Boot 2.0+ 中是如何将 WebMvcConfigurer 添加的拦截器设置到 AbstractHandlerMapping 对象中的呢?接下来开始简单的分析
先来看到 spring-boot-autoconfigure 项目中的 WebMvcAutoConfiguration 自动配置类,其中有一个内部静态类 EnableWebMvcConfiguration,继承了 DelegatingWebMvcConfiguration
对象(spring-webmvc 项目中),部分代码以下:
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(WebProperties.class) public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware { // ... 省略相关代码 }
回到咱们的 spring-webmvc项目,来看到 org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
这个类,继承 WebMvcConfigurationSupport 类,部分代码以下:
@Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { /** WebMvcConfigurer 组合类,内部方法就是遍历全部的 WebMvcConfigurer 实现类 */ private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); @Autowired(required = false) public void setConfigurers(List<WebMvcConfigurer> configurers) { if (!CollectionUtils.isEmpty(configurers)) { // <1> 注入全部的 WebMvcConfigurer 实现类到 configurers 中 this.configurers.addWebMvcConfigurers(configurers); } } @Override protected void addInterceptors(InterceptorRegistry registry) { // <2> 调用 WebMvcConfigurer 组合类的 addInterceptors(InterceptorRegistry registry) 方法 this.configurers.addInterceptors(registry); } } // org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite.java class WebMvcConfigurerComposite implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // <3> 依次执行 WebMvcConfigurer 实现类的 addInterceptors 方法,将对应的拦截器添加至 registry 中 for ( WebMvcConfigurer delegate : this.delegates) { delegate.addInterceptors(registry); } } }
configurers
中,示例中咱们自定义的 InterceptorConfig 就会被注入到这里addInterceptors(InterceptorRegistry registry)
方法,看第 3
步addInterceptors(InterceptorRegistry registry)
方法,将对应的拦截器添加至 registry
中。调用示例中咱们自定义的 InterceptorConfig 方法,则将咱们自定义 JwtInterceptor 拦截器添加至 registry
中了再来看到 org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
这个类,部分代码以下:
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware { @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); mapping.setOrder(0); mapping.setInterceptors(getInterceptors()); // ... 省略相关代码 return mapping; } protected final Object[] getInterceptors() { // 若 interceptors 未初始化,则进行初始化 if (this.interceptors == null) { // 建立 InterceptorRegistry 对象 InterceptorRegistry registry = new InterceptorRegistry(); // 添加拦截器到 interceptors 中 addInterceptors(registry); // 添加内置拦截器到 interceptors 中 registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); // 初始化到 interceptors 属性 this.interceptors = registry.getInterceptors(); } // 若 interceptors 已初始化,则直接返回 return this.interceptors.toArray(); } }
逻辑并不复杂,能够看到 Spring MVC 用到的 RequestMappingHandlerMapping
对象会经过 addInterceptors(InterceptorRegistry registry)
方法,获取到咱们自定义InterceptorConfig中添加的JwtInterceptor 拦截器,并设置到 RequestMappingHandlerMapping
对象中
本文对 Spring MVC 处理请求的过程当中使用到的 HandlerMapping 组件中的 HandlerInterceptor
拦截器进行了分析,DispatcherServlet 在处理请求的过程当中,会执行 HandlerMapping 组件中与请求匹配的拦截器,进行一些拦截处理。拦截器的在项目中会常用到,应用场景比较多,例如权限校验、参数预处理等等,上面也提供了相应的使用示例
拦截器有如下三个方法:
preHandle
:前置处理,在执行方法前执行,所有成功执行才会往下执行方法postHandle
:后置处理,在成功执行方法后执行,倒序afterCompletion
:已完成处理,无论方法是否成功执行都会执行,不过只会执行前置处理成功的拦截器,倒序多个拦截器的执行顺序就是自定义 WebMvcConfigurer 实现类添加拦截器时所加入的顺序
参考文章:芋道源码《精尽 Spring MVC 源码分析》