該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 Spring MVC 原始碼分析 GitHub 地址 進行閱讀
Spring 版本:5.2.4.RELEASE
該系列其他文件請檢視:《精盡 Spring MVC 原始碼分析 - 文章導讀》
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 實現類新增攔截器時所加入的順序
參考文章:芋道原始碼《精盡 Spring MVC 原始碼分析》