Spring5MVC——request與Controller方法映射的創(chuàng)建

SpringMVC的核心流程

  • 建立請求和Controller方法的映射集合的流程。

  • 根據(jù)請求查找對應(yīng)的Controller方法的流程。

  • 請求參數(shù)綁定到方法形參,執(zhí)行方法處理請求,返回結(jié)果進(jìn)行視圖渲染的流程。

HandlerMapping

HandlerMapping接口作用是將請求映射到處理程序,以及預(yù)處理和處理后的攔截器列表,映射是基于一些標(biāo)準(zhǔn)的,其中的細(xì)節(jié)因不同的實(shí)現(xiàn)而不相同。這是官方文檔上一段描述,該接口只有一個方法getHandler(request),返回一個HandlerExecutionChain對象,接口本身很簡單。

public interface HandlerMapping {


    String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";


    String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";


    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";


    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";


    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";


    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";


    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";


    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    // 返回請求的一個處理程序handler和攔截器interceptors
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

DispatcherServlet

DispatcherServlet是Spring MVC核心,它是J2EE規(guī)范前端控制器的實(shí)現(xiàn),負(fù)責(zé)攔截用戶請求,并解析請求進(jìn)行轉(zhuǎn)發(fā)。

public class DispatcherServlet extends FrameworkServlet {

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        //初始化文件上傳處理器
        initMultipartResolver(context);
        
        //初始化國際化配置
        initLocaleResolver(context);

        //初始化主題處理器
        initThemeResolver(context);

        //初始化HanlderMapping
        initHandlerMappings(context);

        //初始化HandlerAdapter
        //HandlerAdapter用來調(diào)用具體的方法對用戶發(fā)來的請求來進(jìn)行處理
        initHandlerAdapters(context);

        //初始化異常處理器,
        // HandlerExceptionResolver是用來對請求處理過程中產(chǎn)生的異常進(jìn)行處理
        initHandlerExceptionResolvers(context);

        //RequestToViewNameTranslator用于在視圖路徑為空的時(shí)候,自動解析請求
        //去獲取ViewName
        initRequestToViewNameTranslator(context);

        //初始化視圖處理器
        //ViewResolvers將邏輯視圖轉(zhuǎn)成view對象
        initViewResolvers(context);

        //FlashMapManager用于存儲、獲取以及管理FlashMap實(shí)例
        initFlashMapManager(context);
    }
}

從方法調(diào)用鏈看,DispatcherServlet的initStrategies方法是在OnRefresh方法之后調(diào)用的,而initHandlerMappings方法是在initStrategies方法中被調(diào)用的。

initHandlerMappings

  • 初始化HanlderMapping
public class DispatcherServlet extends FrameworkServlet {

    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
        //是否檢查所有的HandlersMapping實(shí)現(xiàn)類并載入,默認(rèn)為true
        //<init-param>
        //      <param-name>detectAllHandlerMappings</param-name>
        //      <param-value>false</param-value>
        //</init-param>
        if (this.detectAllHandlerMappings) {
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            //尋找IOC容器中HandlerMapping類型的Bean
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                // 對找到的HandlerMapping類型的Bean列表進(jìn)行排序
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
                //從容器里獲取beanName為handlerMapping的Bean
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }

        // Ensure we have at least one HandlerMapping, by registering
        // a default HandlerMapping if no other mappings are found.
        // 從context上下文中定義HandlerMapping時(shí),Spring MVC將使用默認(rèn)HandlerMapping,默認(rèn)的HandlerMapping在DispatcherServlet.properties屬性文件中定義,
        // 該文件是在DispatcherServlet的static靜態(tài)代碼塊中加載的
        // 默認(rèn)的是:BeanNameUrlHandlerMapping和RequestMappingHandlerMapping
        if (this.handlerMappings == null) {
            //如果以上過程都沒有找到handlerMapping
            //將賦值handlerMappings為默認(rèn)的HandlerMapping
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isTraceEnabled()) {
                logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                        "': using default strategies from DispatcherServlet.properties");
            }
        }
    }
}

這就是HanlderMapping的初始化過程。

HandlerMapping的實(shí)現(xiàn)類

從方法調(diào)用鏈可以得知,SpringMvc有四種HandlerMapping:

  • requestMappingHandlerMapping -> RequestMappingHandlerMapping
  • beanNameHandlerMapping -> BeanNameUrlHandlerMapping
  • routerFunctionMapping -> RouterFunctionMapping
  • defaultServletHandlerMapping -> SimpleUrlHandlerMapping

這里主要關(guān)注RequestMappingHandlerMapping

RequestMappingHandlerMapping繼承類圖
  • 由于RequestMappingHandlerMapping實(shí)現(xiàn)了ApplicationContextAware和ServletContextAware兩個接口,這說明RequestMappingHandlerMapping可以通過這兩個接口獲取到Root容器和Servley子容器中的Bean。

  • RequestMappingHandlerMapping還實(shí)現(xiàn)了InitializingBean接口,該接口的afterPropertiesSet方法是在bean初始化的invokeInitMethods方法之后執(zhí)行的,因此可以在這里加入對標(biāo)記有RequestMapping標(biāo)記的Bean進(jìn)行處理,將相關(guān)的映射關(guān)系依次保存到RequestMappingHandlerMapping或者其父類的成員變量里面。

AbstractHandlerMethodMapping

在類圖中AbstractHandlerMethodMapping實(shí)現(xiàn)了InitializingBean接口,重寫了afterPropertiesSet方法

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
}

從方法調(diào)用鏈可以看出:

  • 程序在Bean初始化后,會立即執(zhí)行AbstractHandlerMethodMapping中的afterPropertiesSet方法,這是因?yàn)镽equestMappingHandlerMapping是需要先在容器中創(chuàng)建出來,后續(xù)才能在DispatcherServlet中的initHandlerMappings方法中去作為HandlerMapping的實(shí)現(xiàn)類給載入到DispatcherServlet的成員變量handlerMappings集合里。

  • 又因?yàn)镽equestMappingHandlerMapping在創(chuàng)建的時(shí)候會去調(diào)用invokeInitMethods方法進(jìn)行初始化,也會執(zhí)行afterPropertiesSet里面的邏輯,也就是會先執(zhí)行initHandlerMethods的邏輯去初始化映射關(guān)系。

AbstractHandlerMethodMapping#initHandlerMethods

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void initHandlerMethods() {
        //遍歷容器里所有的Bean
        for (String beanName : getCandidateBeanNames()) {
            //忽略掉scopedTarget.打頭的bean(session application request之類的作用域內(nèi)的代理類)
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected String[] getCandidateBeanNames() {
        //從root容器以及子容器里,或者僅從子容器里獲取所有的Bean
        return (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
                obtainApplicationContext().getBeanNamesForType(Object.class));
    }
}
  • 根據(jù)detectHandlerMethodsInAncestorContexts變量是否為true,來決定是否需要從root容器以及子容器里,或者僅從子容器里獲取所有的Bean。
  • 如果為false,則只從當(dāng)前的子容器即ServletContext里去查找。
  • 而如果Root容器中有bean被標(biāo)記上RequestMapping的話,detectHandlerMethodsInAncestorContexts就會被標(biāo)記為true。

AbstractHandlerMethodMapping#processCandidateBean

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            //獲取Bean的Class類型
            beanType = obtainApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
            // An unresolvable bean type, probably from a lazy bean - let's ignore it.
            if (logger.isTraceEnabled()) {
                logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
            }
        }
        //判斷Class上是否有Controller注解或是RequestMapping注解
        if (beanType != null && isHandler(beanType)) {
            //提取其url與controller映射關(guān)系
            detectHandlerMethods(beanName);
        }
    }
}


public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {

    @Override
    protected boolean isHandler(Class<?> beanType) {
        //判斷類上是否存在Controller注解或是RequestMapping注解
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
}
  • 首先獲取Bean對應(yīng)的Class對象。
  • 通過判斷Class上是否有Controller注解或是RequestMapping注解,為后續(xù)提取其url與controller映射關(guān)系做好準(zhǔn)備。

AbstractHandlerMethodMapping#detectHandlerMethods

  • 發(fā)覺Controller方法,并建立與請求url的映射關(guān)系。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void detectHandlerMethods(Object handler) {
        //如果handler是字符串,證明是一個beanName,則從IOC容器中獲取其Class對象;否則直接獲取Class對象
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            //為了確保獲取到的類是被代理的類
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            //尋找方法上有@RequestMapping注解的Method實(shí)例
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            if (logger.isTraceEnabled()) {
                logger.trace(formatMappings(userType, methods));
            }
            //將獲取到的Method對象依次注冊到HandlerMapping中去
            methods.forEach((method, mapping) -> {
                //獲取被AOP代理包裝后的方法實(shí)例
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
}

RequestMappingHandlerMapping#getMappingForMethod

  • 創(chuàng)建求映射信息對象RequestMappingInfo。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {

    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        //創(chuàng)建方法上面的RequestMapping信息
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            //創(chuàng)建類上面的RequestMapping信息
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                //將兩個信息合并
                info = typeInfo.combine(info);
            }
            String prefix = getPathPrefix(handlerType);
            if (prefix != null) {
                info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
            }
        }
        return info;
    }

    @Nullable
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        //如果該函數(shù)含有@RequestMapping注解,則根據(jù)其注解信息生成RequestMapping實(shí)例,
        //否則返回空
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        RequestCondition<?> condition = (element instanceof Class ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    }

    protected RequestMappingInfo createRequestMappingInfo(
            RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
        //這里用到了一個典型的建造者模式
        RequestMappingInfo.Builder builder = RequestMappingInfo
                //這里對路徑進(jìn)行解析,在path中是支持SpEL表達(dá)式的,
                //RequestMappingHandlerMapping實(shí)現(xiàn)了EmbeddedValueResolverAware這個接口
                .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
                .methods(requestMapping.method())
                .params(requestMapping.params())
                .headers(requestMapping.headers())
                .consumes(requestMapping.consumes())
                .produces(requestMapping.produces())
                .mappingName(requestMapping.name());
        if (customCondition != null) {
            builder.customCondition(customCondition);
        }
        return builder.options(this.config).build();
    }
}

視線拉回到detectHandlerMethods方法中:

  • 在執(zhí)行完AbstractHandlerMethodMapping類中的detectHandlerMethods方法中的selectMethods方法之后,就能建立起Controller方法實(shí)例和RequestMappingInfo的映射關(guān)系,并將相關(guān)的映射保存到methods這個Map<Method, T>集合中,key為方法實(shí)例,value為RequestMappingInfo實(shí)例。

  • 之后會遍歷methods將相關(guān)的映射信息給注冊到HandlerMapping中。

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void detectHandlerMethods(Object handler) {
        ......

        if (handlerType != null) {
            ......

            //將獲取到的Method對象依次注冊到HandlerMapping中去
            methods.forEach((method, mapping) -> {
                //獲取被AOP代理包裝后的方法實(shí)例
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
}

上面這段代碼,會將methods中的Method實(shí)例和RequestMappingInfo實(shí)例給一一對應(yīng)的注冊到HandlerMapping里面。

AbstractHandlerMethodMapping#registerHandlerMethod

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        this.mappingRegistry.register(mapping, handler, method);
    }

    class MappingRegistry {
        //儲存 MappingRegistration 所有的注冊信息
        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
        //儲存RequestMappingInfo 與 HandlerMethod
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
        //儲存路徑與RequestMappingInfo
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
        //儲存@RequestMapping 注解的請求路徑 與 HandlerMethod列表
        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
        //跨域配置
        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
        //讀寫鎖
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        /**
         * 注冊數(shù)據(jù):  mapping => RequestMappingInfo ||  handler => beanName  ||  method => Method
         *      1、根據(jù) handle 和 method,創(chuàng)建 HandlerMethod,
         *      2、效驗(yàn) HandlerMethod 是否存在
         *      3、儲存 HandlerMethod
         *      4、儲存 RequestMappingInfo 跟 url
         *      5、儲存 @RequestMapping 注解 的路徑跟所有的方法
         *      6、存儲 CorsConfiguration 信息(跨域)
         *      7、儲存 MappingRegistration 對象
         */
        public void register(T mapping, Object handler, Method method) {
            // Assert that the handler method is not a suspending one.
            if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
                    throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
                }
            }
            this.readWriteLock.writeLock().lock();
            try {
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                //驗(yàn)證方法的唯一性,即先前是否已經(jīng)注冊過同樣的映射
                validateMethodMapping(handlerMethod, mapping);
                //注冊RequestMappingInfo 和 HandlerMethod
                this.mappingLookup.put(mapping, handlerMethod);
                //注冊請求路徑與對應(yīng)的RequestMappingInfo
                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    this.urlLookup.add(url, mapping);
                }

                String name = null;
                if (getNamingStrategy() != null) {
                    //注冊請求路徑與HandlerMethod
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    addMappingName(name, handlerMethod);
                }

                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    //注冊HandlerMethod與跨域信息
                    this.corsLookup.put(handlerMethod, corsConfig);
                }
                //創(chuàng)建及注冊 MappingRegistation 信息
                this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            }
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }
    }
}

主要步驟:

  • 1、根據(jù) handle 和 method,創(chuàng)建 HandlerMethod,
  • 2、效驗(yàn) HandlerMethod 是否存在,驗(yàn)證方法的唯一性,即先前是否已經(jīng)注冊過同樣的映射
  • 3、注冊RequestMappingInfo 和 HandlerMethod
  • 4、注冊請求路徑與對應(yīng)的RequestMappingInfo
  • 5、注冊請求路徑與HandlerMethod
  • 6、注冊HandlerMethod與跨域信息
  • 7、創(chuàng)建及注冊 MappingRegistation 信息

最終會將前面獲取到的所有信息給包裝起來,保存到Map<T, MappingRegistration<T>> registry成員變量中,后續(xù)就可以解析請求并選擇合適的Controller方法來對請求進(jìn)行處理。

這樣就完成了建立請求和Controller方法的映射集合的流程的分析。

HandlerAdapter

public interface HandlerAdapter {


    //判斷適配器是否適配handler,適配策略由子類實(shí)現(xiàn)
    boolean supports(Object handler);


    //使用適配的handler執(zhí)行用戶請求
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;


    //返回資源的最后修改時(shí)間,如果handler實(shí)現(xiàn)類不支持可以返回-1
    long getLastModified(HttpServletRequest request, Object handler);

}

以上是HandlerAdapter接口的源碼分析,如需自定義HandlerAdapter,只需要實(shí)現(xiàn)該接口,在supports方法中定義適配策略,并實(shí)現(xiàn)handle方法進(jìn)行調(diào)用即可。

HandlerAdapter的初始化

顧名思義,是handler的適配器,它能處理參數(shù)轉(zhuǎn)換為handler能接受的數(shù)據(jù)類型,解析參數(shù)、處理返回值等。

在DispatcherServlet進(jìn)行初始化流程調(diào)用initStrategies,執(zhí)行完initHandlerMappings方法后,會接著執(zhí)行initHandlerAdapters方法。

initHandlerAdapters

  • 初始化HandlerAdapter
public class DispatcherServlet extends FrameworkServlet {

    private void initHandlerAdapters(ApplicationContext context) {
        this.handlerAdapters = null;
        // 在部署描述文件中可控制該參數(shù)
        if (this.detectAllHandlerAdapters) {
            // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
            // 從應(yīng)用上下文中查找HandlerAdapter
            Map<String, HandlerAdapter> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerAdapters = new ArrayList<>(matchingBeans.values());
                // We keep HandlerAdapters in sorted order.
                // 對使用的HandlerAdapter進(jìn)行排序,spring提供的只有RequestMappingHandlerAdapter實(shí)現(xiàn)了Ordered接口,其他都不具備排序功能
                AnnotationAwareOrderComparator.sort(this.handlerAdapters);
            }
        }
        else {
            try {
                // 如果在部署描述文件中配置了detectAllHandlerAdapters=false,
                // 此時(shí)spring會加載名稱為handlerAdapter的bean為處理器適配器
                HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
                // 轉(zhuǎn)化為集合賦給handlerAdapters屬性
                this.handlerAdapters = Collections.singletonList(ha);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerAdapter later.
            }
        }

        // Ensure we have at least some HandlerAdapters, by registering
        // default HandlerAdapters if no other adapters are found.
        // 如果未配置HandlerAdapter,注冊默認(rèn)的處理器適配器,
        // 即從DispatcherServlet.properties中獲取的HttpRequestHandlerAdapter、
        // SimpleControllerHandlerAdapter和ReqeustMappingHandlerAdapter
        if (this.handlerAdapters == null) {
            this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
            if (logger.isTraceEnabled()) {
                logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
                        "': using default strategies from DispatcherServlet.properties");
            }
        }
    }
}

通過initHandlerAdapters方法的調(diào)用??芍?,一共向List<HandlerAdapter> handlerAdapters集合中注冊了4個HandlerAdapter的實(shí)現(xiàn)類。

  • requestMappingHandlerAdapter -> RequestMappingHandlerAdapter

  • handlerFunctionAdapter -> HandlerFunctionAdapter

  • httpRequestHandlerAdapter -> HttpRequestHandlerAdapter

  • simpleControllerHandlerAdapter -> SimpleControllerHandlerAdapter

以上就是Spring MVC對HandlerAdapter組件的注冊過程。

參考:
https://segmentfault.com/a/1190000014901736

https://segmentfault.com/a/1190000015009343

https://segmentfault.com/a/1190000015027885

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

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