精盡Spring MVC源碼分析 - HandlerMapping 組件(二)之 HandlerInterceptor 攔截器

該系列文檔是本人在學習 Spring MVC 的源碼過程當中總結下來的,可能對讀者不太友好,請結合個人源碼註釋 Spring MVC 源碼分析 GitHub 地址 進行閱讀html

Spring 版本:5.2.4.RELEASEjava

該系列其餘文檔請查看:《精盡 Spring MVC 源碼分析 - 文章導讀》git

HandlerMapping 組件

HandlerMapping 組件,請求的處理器匹配器,負責爲請求找到合適的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptorsgithub

  • handler 處理器是 Object 類型,能夠將其理解成 HandlerMethod 對象(例如咱們使用最多的 @RequestMapping 註解所標註的方法會解析成該對象),包含了方法的全部信息,經過該對象可以執行該方法web

  • HandlerInterceptor 攔截器對處理請求進行加強處理,可用於在執行方法前、成功執行方法後、處理完成後進行一些邏輯處理spring

因爲 HandlerMapping 組件涉及到的內容比較多,考慮到內容的排版,因此將這部份內容拆分紅了四個模塊,依次進行分析:數組

HandlerMapping 組件(二)之 HandlerInterceptor 攔截器

在上一篇《HandlerMapping 組件(一)之 AbstractHandlerMapping》文檔中分析了 HandlerMapping 組件的 AbstractHandlerMapping 抽象類,在獲取HandlerExecutionChain 處理器執行鏈時,會去尋找匹配的 HandlerInterceptor 攔截器們,並添加到其中。那麼本文將分享 Spring MVC 的攔截器相關內容mvc

HandlerInterceptor

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 {
	}
}

HandlerExecutionChain

org.springframework.web.servlet.HandlerExecutionChain,處理器執行鏈,也就是經過 HandlerMapping 組件爲請求找到的處理對象,包含處理器(handler)和攔截器們(interceptorscors

構造方法

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;
}
  1. 得到攔截器數組,經過上面的 getInterceptors() 方法,得到 interceptors 數組

  2. 遍歷 interceptors 攔截器數組

  3. 依次執行攔截器的前置處理

    1. 若是有某個攔截器的前置處理失敗,則調用 triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) 方法,觸發攔截器們的已完成處理,最後返回 false
    2. 每一個攔截器成功執行前置處理後,記錄當前攔截器的位置到 interceptorIndex 屬性中,爲了已完成處理只會執行前置處理成功的攔截器,且倒序執行
  4. 返回 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;
}
  1. 先判斷該路徑是否在不匹配的路徑中
  2. 若是匹配的路徑爲空,則都匹配經過
  3. 判斷路徑是否在須要匹配的路徑中
攔截方法的實現
@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);
		}
	}
}
  1. 注入全部的 WebMvcConfigurer 實現類到 configurers 中,示例中咱們自定義的 InterceptorConfig 就會被注入到這裏
  2. 調用 WebMvcConfigurer 組合類的 addInterceptors(InterceptorRegistry registry) 方法,看第 3
  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 源碼分析》

相關文章
相關標籤/搜索