HandlerMapping 组件
HandlerMapping 组件,请求的 处理器匹配器 ,负责为请求找到合适的 HandlerExecutionChain
处理器执行链,包含处理器(handler
)和拦截器们(interceptors
)
handler
处理器是 Object 类型,可以将其理解成 HandlerMethod 对象(例如我们使用最多的@RequestMapping
注解所标注的方法会解析成该对象),包含了方法的所有信息,通过该对象能够执行该方法HandlerInterceptor
拦截器对处理请求进行 增强处理 ,可用于在执行方法前、成功执行方法后、处理完成后进行一些逻辑处理
由于 HandlerMapping 组件涉及到的内容比较多,考虑到内容的排版,所以将这部分内容拆分成了四个模块,依次进行分析:
- 《HandlerMapping 组件(一)之 AbstractHandlerMapping》
- 《HandlerMapping 组件(二)之 HandlerInterceptor 拦截器》
- 《HandlerMapping 组件(三)之 AbstractHandlerMethodMapping》
- 《HandlerMapping 组件(四)之 AbstractUrlHandlerMapping》
HandlerMapping 组件(二)之 HandlerInterceptor 拦截器
在上一篇 《HandlerMapping 组件(一)之 AbstractHandlerMapping》 文档中分析了 HandlerMapping 组件的 AbstractHandlerMapping 抽象类,在获取HandlerExecutionChain
处理器执行链时,会去寻找匹配的 HandlerInterceptor 拦截器们,并添加到其中。那么本文将分享 Spring MVC 的拦截器相关内容
HandlerInterceptor
org.springframework.web.servlet.HandlerInterceptor
,处理器拦截器接口,代码如下:
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 {
}
}
HandlerExecutionChain
org.springframework.web.servlet.HandlerExecutionChain
,处理器执行链,也就是通过 HandlerMapping 组件为请求找到的处理对象,包含处理器(handler
)和拦截器们(interceptors
)
构造方法
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
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
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
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
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
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
属性, 只会执行前置处理成功的拦截器们 ,因为该属性定义了成功执行前置处理的拦截器的位置 - 如果前置处理没有全部执行成功,或者处理请求的过程中出现异常还是会调用该方法,也就是 会 执行 已完成处理
HandlerInterceptor 的实现类
HandlerMapping 接口体系的结构如下:
可以看到它的实现类有许多,这里来看几个重要的类
MappedInterceptor
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
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
拦截器对应的方法
其他
使用示例
1. <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 上下文中
2. Java Config
在 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);
}
}
}
- 注入所有的 WebMvcConfigurer 实现类到
configurers
中,示例中我们自定义的 InterceptorConfig 就会被注入到这里 - 调用 WebMvcConfigurer 组合类的
addInterceptors(InterceptorRegistry registry)
方法,看第3
步 - 依次执行 WebMvcConfigurer 实现类的
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 实现类添加拦截器时所加入的顺序
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。