springmvc源碼分析-HandlerMapping初始化流程

從本文開始,來分析下springmvc幾個(gè)重要組件是如何工作的。

首選,來看下HandlerMapping

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

這個(gè)接口主要有這個(gè)方法,通過request獲取一個(gè)HandlerExecutionChain對(duì)象,這個(gè)對(duì)象就是包含了兩個(gè)成員變量

//handler就是應(yīng)用程序中定義的controller
    private final Object handler;
//攔截器,在controller方法的執(zhí)行前后加入自己的邏輯,類似于切面
    private final List<HandlerInterceptor> interceptorList = new ArrayList<>();

簡(jiǎn)單的說,HandlerMapping就是通過http請(qǐng)求查詢到對(duì)應(yīng)controller和interceptor。

springmvc默認(rèn)提供的實(shí)現(xiàn)類有三個(gè),分別為RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、RouterFunctionMapping。
日常開發(fā)中用的最多的便是RequestMappingHandlerMapping,因此本文主要來看下這個(gè)這個(gè)bean對(duì)象的初始化流程

初始化流程便是定義在WebMvcConfigurationSupport類中

@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
        @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
        @Qualifier("mvcConversionService") FormattingConversionService conversionService,
        @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    mapping.setOrder(0);
    mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
    mapping.setContentNegotiationManager(contentNegotiationManager);
    mapping.setCorsConfigurations(getCorsConfigurations());

    PathMatchConfigurer pathConfig = getPathMatchConfigurer();
    if (pathConfig.getPatternParser() != null) {
        mapping.setPatternParser(pathConfig.getPatternParser());
    }
    else {
        mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
        mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());

        Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
        if (useSuffixPatternMatch != null) {
            mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
        }
        Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
        if (useRegisteredSuffixPatternMatch != null) {
            mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
        }
    }
    Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
    if (useTrailingSlashMatch != null) {
        mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
    }
    if (pathConfig.getPathPrefixes() != null) {
        mapping.setPathPrefixes(pathConfig.getPathPrefixes());
    }

    return mapping;
}

首先就是設(shè)置order,值越小,越先執(zhí)行。程序中可能存在多個(gè)HandlerMapping實(shí)現(xiàn)類,因此執(zhí)行的先后順序是可以指定的。
然后就是可以添加攔截器interceptor

protected final Object[] getInterceptors(
            FormattingConversionService mvcConversionService,
            ResourceUrlProvider mvcResourceUrlProvider) {

    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();
}

springmvc默認(rèn)會(huì)添加兩個(gè)攔截器ConversionServiceExposingInterceptor和ResourceUrlProviderExposingInterceptor,這里有一個(gè)很重要的方法addInterceptors,這方法就是給子類繼承重寫,讓應(yīng)用程序可以手動(dòng)添加interceptor

每個(gè)攔截器是一個(gè)InterceptorRegistration對(duì)象,包含了三個(gè)成員變量

//攔截器對(duì)象
    private final HandlerInterceptor interceptor;
//符合攔截器執(zhí)行的路徑模式
    private final List<String> includePatterns = new ArrayList<>();
//符合攔截器執(zhí)行的路徑模式
    private final List<String> excludePatterns = new ArrayList<>();
//路徑匹配器
    @Nullable
    private PathMatcher pathMatcher;
//攔截器的執(zhí)行順序
    private int order = 0;

HandlerMapping除了添加攔截器外,還有一個(gè)比較重要的方法,是添加跨域信息相關(guān)的,同樣有個(gè)方法addCorsMappings可以讓子類重寫,由應(yīng)用程序添加跨域相關(guān)的配置。

HandlerMapping其他屬性由于應(yīng)用程序一般不需要去修改,因此這里不再講述了,當(dāng)生成RequestMappingHandlerMapping對(duì)象后,由于這個(gè)類還實(shí)現(xiàn)了InitializingBean接口,因此還會(huì)執(zhí)行初始化方法afterPropertiesSet

    public void afterPropertiesSet() {
        this.config = new RequestMappingInfo.BuilderConfiguration();
        this.config.setUrlPathHelper(getUrlPathHelper());
        this.config.setPathMatcher(getPathMatcher());
        this.config.setSuffixPatternMatch(useSuffixPatternMatch());
        this.config.setTrailingSlashMatch(useTrailingSlashMatch());
        this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
        this.config.setContentNegotiationManager(getContentNegotiationManager());

        super.afterPropertiesSet();
    }

可以看到這個(gè)方法主要是將HandlerMapping的相關(guān)屬性值賦值給內(nèi)部成員RequestMappingInfo.BuilderConfiguration的屬性,然后再調(diào)用initHandlerMethods方法

protected void initHandlerMethods() {
    for (String beanName : getCandidateBeanNames()) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            processCandidateBean(beanName);
        }
    }
}

這個(gè)方法中,循環(huán)處理容器的beanName列表,然后取出beanName對(duì)應(yīng)的Class對(duì)象,判斷該class對(duì)象是否為Handler,判斷標(biāo)準(zhǔn)就是看類是否有注解了Controller或者RequestMapping,若是Handler,則執(zhí)行detectHandlerMethods

protected void detectHandlerMethods(Object handler) {
    Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                    try {
//根據(jù)Method對(duì)象,獲取對(duì)應(yīng)的RequestMappingInfo對(duì)象
                        return getMappingForMethod(method, userType);
                    }
                    catch (Throwable ex) {
                        throw new IllegalStateException("Invalid mapping on handler class [" +
                                userType.getName() + "]: " + method, ex);
                    }
                });
        //....中間日志省略
        methods.forEach((method, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

這個(gè)方法主要分為兩個(gè)步驟

  1. MethodIntrospector.selectMethods 獲取handler(controller)下Method對(duì)象及其對(duì)應(yīng)的RequestMappingInfo對(duì)象,其中RequestMappingInfo對(duì)象就是對(duì)方法注解@RequestMapping相關(guān)屬性值的封裝(也會(huì)結(jié)合注解在類上面的@RequestMapping)
  2. 將Method對(duì)象,RequestMappingInfo對(duì)象維護(hù)到HandlerMapping中,當(dāng)然還有包含url相關(guān)的,這樣后續(xù)請(qǐng)求到來,就可根據(jù)url映射出對(duì)應(yīng)的handler。

上述步驟1涉及到的細(xì)節(jié)很多,這里不展開了,主要來看下步驟2,registerHandlerMethod的處理

public void register(T mapping, Object handler, Method method) {
            this.readWriteLock.writeLock().lock();
            try {
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                validateMethodMapping(handlerMethod, mapping);
                this.mappingLookup.put(mapping, handlerMethod);

                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    this.urlLookup.add(url, mapping);
                }

                String name = null;
                if (getNamingStrategy() != null) {
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    addMappingName(name, handlerMethod);
                }

                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    this.corsLookup.put(handlerMethod, corsConfig);
                }

                this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            }
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }

這方法主要是對(duì)HandlerMapping內(nèi)部一些map的維護(hù)

//RequestMappingInfo,MappingRegistration
        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
//RequestMappingInfo, HandlerMethod
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
//url, RequestMappingInfo列表
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
//name, HandlerMethod
        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
//cors
        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

通過這些map,程序可以快速的根據(jù)請(qǐng)求url定位到具體的handler,再執(zhí)行對(duì)應(yīng)的方法。這個(gè)請(qǐng)求的流程后續(xù)再講述。

總結(jié),RequestMapping實(shí)例化主要是對(duì)內(nèi)部重要的屬性進(jìn)行賦值,方便應(yīng)用程序可根據(jù)http請(qǐng)求的相關(guān)信息,比如url,header頭等等,找到對(duì)應(yīng)的Controller,并執(zhí)行相應(yīng)的方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容