SpringMVC的設計與實現(xiàn)

SpringMVC的設計與實現(xiàn)

? Spring MVC是MVC模式的一種實現(xiàn) , 在Spring MVC 的使用中 , 除了之前講過的ContextLoaderListener , 還有一個比較重要的類DispatchServlet , 在web.xml中也對其進行了配置 。從名字可以看出來這是一個Servlet , 這個Servlet實現(xiàn)的是Sun的J2EE核心模式中的前端控制器模式(Front Controller) , 所有的請求都要經(jīng)過它來處理 , 進行轉(zhuǎn)發(fā)、匹配、數(shù)據(jù)處理后 , 并轉(zhuǎn)由頁面進行展現(xiàn) , 因此可以把它當做Spring MVC視線中最為核心的部分。

? DispatchServlet與ContextLoaderListener 有沒有什么聯(lián)系呢 ?

? 兩者都是Spring上下文體系中非常重要的部分 , 在完成ContextLoaderListener的初始化后 , Web容器開始初始化DispatchServlet 。DispatchServlet會建立自己的上下文來持有Spring MVC的Bean對象 , 它會將ServletContext中的根上下文作為它的雙親上下文 , 并把自己持有的上下文也進行初始化并保存到ServletContext中。

? DispatchServlet是怎么對上下文進行初始化的呢?先看看他的類繼承關系圖

image.png
DispatchServlet通過繼承FramewServlet和HttpServletBean而繼承了HttpServlet , 通過使用Servlet API來對HTTP請求進行響應 , 成為Spring MVC的前端處理器 , 同時成為MVC模塊與Web容器集成的處理前端。
image.png

? 從上圖中可以看到Dispatchservlet的工作可以分為兩部分: 1.初始化部分 , 由initServletBean()啟動 , 通過initWebApplicationContext()方法最終調(diào)用DispatchServlet的initStrategies()方法 , 這部分做的就是上下文的初始化部分。2.對HTTP請求進行響應 , doService()方法會調(diào)用doDispatch()方法 , 在這個方法中進行了轉(zhuǎn)發(fā)的操作。

1.Dispatchservlet的啟動和初始化
? 作為Servlet , 初始化時init()方法會被調(diào)用 , init方法中調(diào)用了子類的initServletBean()方法


public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // 獲取Servlet的初始化參數(shù),對Bean屬性進行配置
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                if (logger.isErrorEnabled()) {
                    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                }
                throw ex;
            }
        }
// 調(diào)用子類的initServletBean進行具體的初始化
    initServletBean();
    if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }



protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
            //初始化上下文
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        }
        catch (ServletException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }

        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                    elapsedTime + " ms");
        }
    }
protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;

        if (this.webApplicationContext != null) {
            // A context instance was injected at construction time -> use it
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent -> set
                        // the root application context (if any; may be null) as the parent
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            // No context instance was injected at construction time -> see if one
            // has been registered in the servlet context. If one exists, it is assumed
            // that the parent context (if any) has already been set and that the
            // user has performed any initialization such as setting the context id
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            // 創(chuàng)建上下文的過程和IoC很相似 , 沒有指定就創(chuàng)建默認的上下文
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            // Either the context is not a ConfigurableApplicationContext with refresh
            // support or the context injected at construction time had already been
            // refreshed -> trigger initial onRefresh manually here.
            onRefresh(wac);
        }

        if (this.publishContext) {
            // Publish the context as a servlet context attribute.
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

DispatchServlet持有自己的一個Servlet名稱命名的IoC容器 , 這個IoC容器是一個WebApplicationContext對象 , SpringMVC的具體實現(xiàn)和Spring應用的實現(xiàn)并沒有什么太大差別 . 但是MVC在對容器初始化完成之后會調(diào)用initStrategies()方法,來實現(xiàn)轉(zhuǎn)發(fā)功能的初始化 , 可以從名稱看出來 ,這個方法中初始化了支持request映射的HandlerMapping、視圖、國際化的localResolver等 .

protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }   

initHandlerMappings方法并不是干初始化Mapping的工作的 , 它只是初始化handlerMappings屬性的值 , 決定后續(xù)的轉(zhuǎn)發(fā)是由哪個子類執(zhí)行的

private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;

        if (this.detectAllHandlerMappings) {
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            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.
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
                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.
            }
        }

        // 默認加載的是DispatcherServlet.properties文件中的內(nèi)容
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }

? DispatcherServlet.properties

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

至于Controller是什么時候轉(zhuǎn)換成Mapping的 , 可以研究一下AbstractHandlerMethodMapping類 , 從名字可以看出來該類是將method作為handler來使用的

// @since 3.1  Spring3.1之后才出現(xiàn),這個時候注解驅(qū)動也出來了
// 實現(xiàn)了initializingBean接口,其實主要的注冊操作則是通過afterPropertiesSet()接口方法來調(diào)用的
// 它是帶有泛型T的。
// T:包含HandlerMethod與傳入請求匹配所需條件的handlerMethod的映射~
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    // SCOPED_TARGET的BeanName的前綴
    private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";
    private static final HandlerMethod PREFLIGHT_AMBIGUOUS_MATCH = new HandlerMethod(new EmptyHandler(), ClassUtils.getMethod(EmptyHandler.class, "handle"));
    // 跨域相關
    private static final CorsConfiguration ALLOW_CORS_CONFIG = new CorsConfiguration();
    static {
        ALLOW_CORS_CONFIG.addAllowedOrigin("*");
        ALLOW_CORS_CONFIG.addAllowedMethod("*");
        ALLOW_CORS_CONFIG.addAllowedHeader("*");
        ALLOW_CORS_CONFIG.setAllowCredentials(true);
    }
    
    // 默認不會去祖先容器里面找Handlers
    private boolean detectHandlerMethodsInAncestorContexts = false;
    // @since 4.1提供的新接口
    // 為處HandlerMetho的映射分配名稱的策略接口   只有一個方法getName()
    // 唯一實現(xiàn)為:RequestMappingInfoHandlerMethodMappingNamingStrategy
    // 策略為:@RequestMapping指定了name屬性,那就以指定的為準  否則策略為:取出Controller所有的`大寫字母` + # + method.getName()
    // 如:AppoloController#match方法  最終的name為:AC#match 
    // 當然這個你也可以自己實現(xiàn)這個接口,然后set進來即可(只是一般沒啥必要這么去干~~)
    @Nullable
    private HandlerMethodMappingNamingStrategy<T> namingStrategy;
    // 內(nèi)部類:負責注冊~
    private final MappingRegistry mappingRegistry = new MappingRegistry();

    // 此處細節(jié):使用的是讀寫鎖  比如此處使用的是讀鎖   獲得所有的注冊進去的Handler的Map
    public Map<T, HandlerMethod> getHandlerMethods() {
        this.mappingRegistry.acquireReadLock();
        try {
            return Collections.unmodifiableMap(this.mappingRegistry.getMappings());
        } finally {
            this.mappingRegistry.releaseReadLock();
        }
    }
    // 此處是根據(jù)mappingName來獲取一個Handler  此處需要注意哦~~~
    @Nullable
    public List<HandlerMethod> getHandlerMethodsForMappingName(String mappingName) {
        return this.mappingRegistry.getHandlerMethodsByMappingName(mappingName);
    }
    // 最終都是委托給mappingRegistry去做了注冊的工作   此處日志級別為trace級別
    public void registerMapping(T mapping, Object handler, Method method) {
        if (logger.isTraceEnabled()) {
            logger.trace("Register \"" + mapping + "\" to " + method.toGenericString());
        }
        this.mappingRegistry.register(mapping, handler, method);
    }
    public void unregisterMapping(T mapping) {
        if (logger.isTraceEnabled()) {
            logger.trace("Unregister mapping \"" + mapping + "\"");
        }
        this.mappingRegistry.unregister(mapping);
    }

    // 這個很重要,是初始化HandlerMethods的入口~~~~~
    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
    // 看initHandlerMethods(),觀察是如何實現(xiàn)加載HandlerMethod
    protected void initHandlerMethods() {
        // getCandidateBeanNames:Object.class相當于拿到當前容器(一般都是當前容器) 內(nèi)所有的Bean定義信息
        // 如果閣下容器隔離到到的話,這里一般只會拿到@Controller標注的web組件  以及其它相關web組件的  不會非常的多的~~~~
        for (String beanName : getCandidateBeanNames()) {
            // BeanName不是以這個打頭得  這里才會process這個BeanName~~~~
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                // 會在每個Bean里面找處理方法,HandlerMethod,然后注冊進去
                processCandidateBean(beanName);
            }
        }
        // 略:它就是輸出一句日志:debug日志或者trace日志   `7 mappings in 'requestMappingHandlerMapping'`
        handlerMethodsInitialized(getHandlerMethods());
    }

    // 確定指定的候選bean的類型,如果標識為Handler類型,則調(diào)用DetectHandlerMethods
    // isHandler(beanType):判斷這個type是否為Handler類型   它是個抽象方法,由子類去決定到底啥才叫Handler~~~~
    // `RequestMappingHandlerMapping`的判斷依據(jù)為:該類上標注了@Controller注解或者@Controller注解  就算作是一個Handler
    // 所以此處:@Controller起到了一個特殊的作用,不能等價于@Component的喲~~~~
    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            beanType = obtainApplicationContext().getType(beanName);
        } catch (Throwable ex) {
            // 即使拋出異常  程序也不會終止~
        }
        if (beanType != null && isHandler(beanType)) {
            // 這個和我們上篇博文講述的類似,都屬于detect探測系列~~~~
            detectHandlerMethods(beanName);
        }
    }

    // 在指定的Handler的bean中查找處理程序方法Methods  找打就注冊進去:mappingRegistry
    protected void detectHandlerMethods(Object handler) {
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
        
            // 又是非常熟悉的方法:MethodIntrospector.selectMethods
            // 它在我們招@EventListener、@Scheduled等注解方法時已經(jīng)遇到過多次
            // 此處特別之處在于:getMappingForMethod屬于一個抽象方法,由子類去決定它的尋找規(guī)則~~~~  什么才算作一個處理器方法
            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);
                        }
                    });
            
            // 把找到的Method  一個個遍歷,注冊進去~~~~
            methods.forEach((method, mapping) -> {
                // 找到這個可調(diào)用的方法(AopUtils.selectInvocableMethod)
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
}

大致步驟: 找到容器中的所有bean , 如果bean是handler的話就就對bean進行解析 (是否是handler是交給子類去實現(xiàn)的) , 如果是handler獲取到該bean中的所有method , (以RequestMappingHandlerMapping為例 ),如果方法上面有requestMapping,根據(jù)注解的信息生成RequestMappingInfo , 并且將方法上的信息與類上面的信息結合 , 將方法與mapping信息的對應關系存到Map中 ,遍歷Map為method找到對應的可調(diào)用方法invocableMethod , 然后將方法注冊到HandlerMapping中 , 注冊存儲的地方是AbstractHandlerMethodMapping.MappingRegistry:內(nèi)部類注冊中心

class MappingRegistry {
        // mapping對應的其MappingRegistration對象~~~
        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
        // 保存著mapping和HandlerMethod的對應關系~
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
        // 保存著URL與匹配條件(mapping)的對應關系  當然這里的URL是pattern式的,可以使用通配符
        // 這里的Map不是普通的Map,而是MultiValueMap,它是個多值Map。其實它的value是一個list類型的值
        // 至于為何是多值?有這么一種情況  URL都是/api/v1/hello  但是有的是get post delete等方法   所以有可能是會匹配到多個MappingInfo的
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
        // 這個Map是Spring MVC4.1新增的(畢竟這個策略接口HandlerMethodMappingNamingStrategy在Spring4.1后才有,這里的name是它生成出來的)
        // 保存著name和HandlerMethod的對應關系(一個name可以有多個HandlerMethod)
        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
        
        // 這兩個就不用解釋了
        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
        // 讀寫鎖~~~ 讀寫分離  提高啟動效率
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        ... // 提供一些查找方法,都不是線程安全的
        
        // 讀鎖提供給外部訪問,寫鎖自己放在內(nèi)部即可~~~
        public void acquireReadLock() {
            this.readWriteLock.readLock().lock();
        }
        public void releaseReadLock() {
            this.readWriteLock.readLock().unlock();
        }

        // 注冊Mapping和handler 以及Method    此處上寫鎖保證線程安全~
        public void register(T mapping, Object handler, Method method) {
            this.readWriteLock.writeLock().lock();
            try {
                // 此處注意:都是new HandlerMethod()了一個新的出來~~~~
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                // 同樣的:一個URL Mapping只能對應一個Handler
                // 這里可能會出現(xiàn)常見的一個異常信息:Ambiguous mapping. Cannot map XXX 
                assertUniqueMethodMapping(handlerMethod, mapping);
        
                // 緩存Mapping和handlerMethod的關系  
                this.mappingLookup.put(mapping, handlerMethod);

                // 保存url和RequestMappingInfo(mapping)對應關系
                // 這里注意:多個url可能對應著同一個mappingInfo呢~  畢竟@RequestMapping的url是可以寫多個的~~~~
                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    this.urlLookup.add(url, mapping);
                }

                // 保存name和handlerMethod的關系  同樣也是一對多
                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);
                }

                // 注冊mapping和MappingRegistration的關系
                this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            }
            // 釋放鎖
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }

        // 相當于進行一次逆向操作~
        public void unregister(T mapping) { ... }
        ...
    }

參考:https://cloud.tencent.com/developer/article/1497621

注冊完成之后DispatchServlet的啟動過程中的重要部分就結束了 ,下面看一下調(diào)用過程

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

DispatchServlet也是一個servlet ,我們知道servlet的調(diào)用方法入口是doService()方法,在doService()方法中又調(diào)用了dodispatch()方法,在這個這個方法中又調(diào)用了getHandler()方法 , 根據(jù)請求的路徑找到對應的handler , 這個方法是請求分發(fā)過程中比較重要的部分

DispatchServlet類:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping hm : this.handlerMappings) {
                if (logger.isTraceEnabled()) {
                    logger.trace(
                            "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
                }
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

AbstractHandlerMapping類:

獲取到handle并且把攔截器封裝到HandlerExecutionChain中 , 以便后面使用

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;
    }
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    ...
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        // 要進行匹配的  請求的URI path
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        this.mappingRegistry.acquireReadLock();
        try {
            //委托給方法lookupHandlerMethod() 去找到一個HandlerMethod去最終處理~
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }
    @Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        // Match是一個private class,內(nèi)部就兩個屬性:T mapping和HandlerMethod handlerMethod
        List<Match> matches = new ArrayList<>();
        
        // 根據(jù)lookupPath去注冊中心里查找mappingInfos,因為一個具體的url可能匹配上多個MappingInfo的
        // 至于為何是多值?有這么一種情況  URL都是/api/v1/hello  但是有的是get post delete等方法  當然還有可能是headers/consumes等等不一樣,都算多個的  所以有可能是會匹配到多個MappingInfo的
        // 所有這個里可以匹配出多個出來。比如/hello 匹配出GET、POST、PUT都成,所以size可以為3
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        
        if (directPathMatches != null) {
            // 依賴于子類實現(xiàn)的抽象方法:getMatchingMapping()  看看到底匹不匹配,而不僅僅是URL匹配就行
            // 比如還有method、headers、consumes等等這些不同都代表著不同的MappingInfo的
            // 最終匹配上的,會new Match()放進matches里面去
            addMatchingMappings(directPathMatches, matches, request);
        }
    
        // 當還沒有匹配上的時候,別無選擇,只能瀏覽所有映射
        // 這里為何要瀏覽所有的mappings呢?而不是報錯404呢?這里我有點迷糊,愿有知道的指明這個設計意圖~~~
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        // 單反只要找到了一個匹配的  就進來這里了~~~
        // 請注意:因為到這里   匹配上的可能還不止一個  所以才需要繼續(xù)處理~~
        if (!matches.isEmpty()) {
            // getMappingComparator這個方法也是抽象方法由子類去實現(xiàn)的。
            // 比如:`RequestMappingInfoHandlerMapping`的實現(xiàn)為先比較Method,patterns、params
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            matches.sort(comparator);
            // 排序后的最佳匹配為get(0)
            Match bestMatch = matches.get(0);
    
            // 如果總的匹配個數(shù)大于1的話
            if (matches.size() > 1) {
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
        
                // 次最佳匹配
                Match secondBestMatch = matches.get(1);
                // 如果發(fā)現(xiàn)次最佳匹配和最佳匹配  比較是相等的  那就報錯吧~~~~
                // Ambiguous handler methods mapped for~~~
                // 注意:這個是運行時的檢查,在啟動的時候是檢查不出來的~~~  所以運行期的這個檢查也是很有必要的~~~   否則就會出現(xiàn)意想不到的效果
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    String uri = request.getRequestURI();
                    throw new IllegalStateException(
                            "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                }
            }
            // 把最最佳匹配的方法  放進request的屬性里面~~~
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            // 它也是做了一件事:request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath)
            handleMatch(bestMatch.mapping, lookupPath, request);
            // 最終返回的是HandlerMethod~~~
            return bestMatch.handlerMethod;
        }
        // 一個都沒匹配上,handleNoMatch這個方法雖然不是抽象方法,protected方法子類復寫
        // RequestMappingInfoHandlerMapping有復寫此方法~~~~
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }
    ...

    // 因為上面說了mappings可能會有多個,比如get post put的都算~~~這里就是要進行篩選出所有match上的
    private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
        for (T mapping : mappings) {
            // 只有RequestMappingInfoHandlerMapping 實現(xiàn)了一句話:return info.getMatchingCondition(request);
            // 因此RequestMappingInfo#getMatchingCondition() 方法里大有文章可為~~~
            // 它會對所有的methods、params、headers... 都進行匹配  但凡匹配不上的就返回null  
            T match = getMatchingMapping(mapping, request);
            if (match != null) {
                matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
            }
        }
    }
}

// ===============RequestMappingInfo 的源碼部分講解================
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
    
    // 這些個匹配器都繼承自AbstractRequestCondition,會進行各自的匹配工作  
    // 下面會以PatternsRequestCondition為例進行示例講解~~~~~
    // 他們頂級抽象接口為:RequestCondition  @since 3.1 :Contract for request mapping conditions
    private final PatternsRequestCondition patternsCondition;
    private final RequestMethodsRequestCondition methodsCondition;
    private final ParamsRequestCondition paramsCondition;
    private final HeadersRequestCondition headersCondition;
    private final ConsumesRequestCondition consumesCondition;
    private final ProducesRequestCondition producesCondition;
    private final RequestConditionHolder customConditionHolder;

    // 因為類上和方法上都可能會有@RequestMapping注解,所以這里是把語意思合并  該方法來自頂層接口
    @Override
    public RequestMappingInfo combine(RequestMappingInfo other) {
        String name = combineNames(other);
        PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
        RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
        ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
        HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
        ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
        ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
        RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);

        return new RequestMappingInfo(name, patterns,
                methods, params, headers, consumes, produces, custom.getCondition());
    }

    // 合并后,就開始發(fā)揮作用了,該接口來自于頂層接口~~~~
    @Override
    @Nullable
    public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
        RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
        if (methods == null) {
            return null;
        }
        ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
        if (params == null) {
            return null;
        }
        HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
        if (headers == null) {
            return null;
        }
        ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
        if (consumes == null) {
            return null;
        }
        ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
        if (produces == null) {
            return null;
        }
        PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
        if (patterns == null) {
            return null;
        }
        RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
        if (custom == null) {
            return null;
        }

        return new RequestMappingInfo(this.name, patterns,
                methods, params, headers, consumes, produces, custom.getCondition());
    }
}
?   找到對應的handler之后根據(jù)handler找對應的HandlerAdapter, 我們使用的requestMapping對應的adapter是RequestMappingHandlerAdapter

?   執(zhí)行所有注冊攔截器的preHandler方法, 該方法中調(diào)用所有攔截器的preHandle方法,如果preHandle返回true繼續(xù)執(zhí)行下一個攔截器 ,否則調(diào)用triggerAfterCompletion方法 , triggerAfterCompletion會倒敘調(diào)用已經(jīng)執(zhí)行過得攔截器的afterCompletion的方法。

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

可以看出,以上方法就是遍歷處理入?yún)?,但是可以從這個代碼中引出一個概念叫做:HandlerMethodArgumentResolver,HandlerMethod的參數(shù)解析器。

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

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