Springboot 攔截器的背后


今天寫了個攔截器對一些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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM