今天寫了個攔截器對一些mapping做了些處理,寫完之后突然很想看看攔截器是怎么加進spring里面。對着源碼debug了一遍。又有了新的收獲。
1.攔截器的實現
1.實現HandlerInterceptor
public class MyHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("---------preHandle--------"); return true; } /** * controller執行之后,且頁面渲染之前調用 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("---------postHandle--------"); } /** * 頁面渲染之后調用,一般用於資源清理操作 * @param request * @param response * @param handler * @param ex * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("---------afterCompletion--------"); }
2.將攔截器加入到攔截鏈里面去,這里可以實現
WebMvcConfigurer
也可以繼承
WebMvcConfigurerAdapter
只是 WebMvcConfigurerAdapter這個類在Springboot2.0已經 Deprecated了,這部分內容我們后面再講
@Component public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyHandlerInterceptor()); } }
接下來我們看看攔截是怎么被調用的,在 preHandle方法打斷點
我們發現攔截器的獲取在 org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle 方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
這里的 getInterceptors 如下所示
public HandlerInterceptor[] getInterceptors() { if (this.interceptors == null && this.interceptorList != null) { this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]); } return this.interceptors; }
那現在的問題就是要找到 interceptors是怎么初始化的呢。我們找到了HandlerExecutionChain的構造方法,發現interceptors就是在這賦值的
public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) { if (handler instanceof HandlerExecutionChain) { HandlerExecutionChain originalChain = (HandlerExecutionChain) handler; this.handler = originalChain.getHandler(); this.interceptorList = new ArrayList<>(); CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList); CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList); } else { this.handler = handler; this.interceptors = interceptors; } }
再在這打個斷點,找到了調用這個構造方法的類
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
看到 AbstractHandlerMapping 差不多就知道是怎么一回事情了,這里再把調用的代碼貼出來
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
這個getHandler方法 其實就是RequestMapping注解調用的地方,這里的handle可以想象成是一個controller,getHandlerExecutionChain 這個方法的作用就是給我們的controller加上一層攔截器的屬性,從HandlerExecutionChain的構造方法也能看出,HandlerExecutionChain 就是 handle和interceptor的封裝。
到這里,我們大概是知道了攔截器是怎么被調用的。但是,我們還不知道攔截器是怎么被加載進spring的呢?
這里我們將重點放在 getHandlerExecutionChain 的 this.adaptedInterceptors 屬性
我們找到了這個方法
protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } }
這里的interceptor又是從interceptors獲取而來,interceptors 的初始化是通過以下代碼
public void setInterceptors(Object... interceptors) { this.interceptors.addAll(Arrays.asList(interceptors)); }
我們在這里打個斷點,最終找到了
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerMapping
public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); mapping.setOrder(0); mapping.setInterceptors(getInterceptors()); mapping.setContentNegotiationManager(mvcContentNegotiationManager()); mapping.setCorsConfigurations(getCorsConfigurations());
protected final Object[] getInterceptors() { if (this.interceptors == null) { InterceptorRegistry registry = new InterceptorRegistry(); addInterceptors(registry); registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); this.interceptors = registry.getInterceptors(); } return this.interceptors.toArray(); }
還記最開始我們說的 將攔截器加入到攔截鏈里面的方法么。就是在這里調用的。
public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyHandlerInterceptor()); } }
到這里我們大概的就知道了攔截器是怎么加入spring的。還剩最后一個問題,requestMappingHandlerMapping 是由怎么觸發的呢?
我們找到了方法的調用
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration#requestMappingHandlerMapping
@Bean @Primary @Override public RequestMappingHandlerMapping requestMappingHandlerMapping() { // Must be @Primary for MvcUriComponentsBuilder to work return super.requestMappingHandlerMapping(); }
這個方法在 WebMvcAutoConfiguration里面,看到這個類名就知道這是個自動配置類。那么他一定和@EnableAutoconfigure 注解有關。我在 org/springframework/boot/spring-boot-autoconfigure/2.0.4.RELEASE/spring-boot-autoconfigure-2.0.4.RELEASE.jar!/META-INF/spring.factories 這個文件里面找到了AutoConfig的配置。所以 requestMappingHandlerMapping 是通過springboot自動配置掃描bean加載的。
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
最后 我們再看下 WebMvcAutoConfiguration這個類的幾個注解
@Configuration @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration {
ConditionalOnMissingBean這個注解表明 只有不存在 WebMvcConfigurationSupport 這個bean才可以配置加載,所以這也是為什么我們在將攔截器加入到攔截鏈里面的方法里面是實現 WebMvcConfigurer 而不是繼承WebMvcConfigurationSupport。
最后我們再總結下
1.項目啟動的時候springboot會自動掃描相關配置類觸發requestMappingHandlerMapping方法
2.requestMappingHandlerMapping會將系統的各個攔截添加到攔截器數組中,真正的http請求過來后會調用getHandler方法將過濾器和handle封裝成HandlerExecutionChain。
3.按照過濾器添加順序依次執行過濾器
以上,就是對攔截器的分析
轉載請注明出處 https://www.cnblogs.com/xmzJava/p/9550535.html