Spring源碼分析之Spring MVC

Spring MVC 源碼分析

一、Spring MVC運行流程圖

微信圖片_20200916144854.png

二、源碼分析

第一步:發(fā)送請求

這里我們需要清楚的知道spring mvc的入口是org.springframework.web.servlet.DispatcherServlet#doDispatch()。

DispatcherServlet是SpringMVC中的前端控制器,負(fù)責(zé)接收request并將request轉(zhuǎn)發(fā)給對應(yīng)的處理組件。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {}

可以看到參數(shù)是HttpServletRequest,HttpServletResponse,專門來處理請求,和相應(yīng)的。我們可以查看類圖清晰的知道其實DispatcherServlet也是一個實現(xiàn)了HtttServlet接口的,是一個標(biāo)準(zhǔn)的Servlet容器。

image-20220712173459673.png

第二步:獲取handler

HanlerMapping是SpringMVC中完成url到Controller映射的組件,也就是每個url都有一個對應(yīng)的Handler去處理,而對應(yīng)的handler里有對應(yīng)的Controller。

首先,需要知道的是都有哪些handler,為什么會有這么多handler

  • 有哪些handler
image-20220712172229524.png

首先有這么多的handler,這個是在spring boot下進行的調(diào)試,如果只是單單的使用spring mvc,可能會少一個。

  • 為什么有這么多的handler

    因為歷史原因,Spring MVC 在發(fā)展的過程中有很多種寫法,其中最熟悉的寫法如下:

    @RestController
    public class UserController {
        @GetMapping("/hello")
        public String sayHello() {
            return "hello";
        }
    
        @PostMapping("/bye")
        public String sayBye(){
            return "bye";
        }
    }
    

    但是在spring boot出現(xiàn)之前,或者在spring的早期版本中,還有如下寫法也可以處理客戶端請求

    @Component("/demo3")
    public class ControllerDemo3 implements HttpRequestHandler {
        @Override
        public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            WebApplicationContext webApplicationContext = RequestContextUtils.findWebApplicationContext(request);
            System.out.println("===實現(xiàn)HttpRequestHandler接口===");
        }
    }
    

    亦或是

    @Component("/demo2")
    public class MyController implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            System.out.println("123123");
            return null;
        }
    }
    

    亦或是

    @Component("/demo4")
    public class TemproController {
        public String sayHello(){
            return "demo4";
        }
    }
    

    每種寫法的背后都需要一種對應(yīng)的處理器去處理,因為寫法的不通,所以早就了這么多的handler。

其次,需要看spring 是如何查找handler的

  1. 在講解如何查找handler之前,需要先了解這里的設(shè)計結(jié)構(gòu)

    看第三部分HandlerMapping

  1. 具體查找如下,可以看到doDispatch方法如下
image-20220713095828508.png

getHandler()方法如下

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            /**
            * 通過for循環(huán)在所有的handlerMappings里查找對應(yīng)的handler。
            * 怎么查找繼續(xù)看mapping.getHandler()
            * 
            */
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request);
    ......
}
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
       //這里可以看到lookupPath就是我們請求的路徑 如:/demo3
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        request.setAttribute(LOOKUP_PATH, lookupPath);
        this.mappingRegistry.acquireReadLock();
        try {
            //這里lookupHandlerMethod就是查找handler的地方
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
}

重點來了,這里就是最終查找handler的方法

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        //①
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                matches.sort(comparator);
                bestMatch = matches.get(0);
                if (logger.isTraceEnabled()) {
                    logger.trace(matches.size() + " matching mappings: " + matches);
                }
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                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.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

①處調(diào)試代碼如下:

image-20220713100608789.png

可以看到所有的請求地址都在LinkedMultivalueMap中,這里沒有匹配到,如果沒有匹配到最后

image-20220713100835501.png

,這里在第二輪的查找中找到了,如下:

image-20220713101127057.png

這里找到handler之后,在看是如何返回的,返回的過程中,有沒有對此ControdderDemo3做過什么修改。我們看到最后return了一個buildPathExposingHandler。這個方法具體做了什么,我們接著往下看

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
                                          String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
    
    /**
    * 這里使用了HandlerExecutionChain把原有的處理器給包裹了,并暴露請求的路徑地址以及uri模板變量。
    * 這里為什么包裹了,后面再說......
    */
    HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
    chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
    if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
        chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
    }
    return chain;
}

也就是說這里返回了一個包含原有處理器的HandlerExecutionChain,并且HandlerExecutionChain內(nèi)新增了了一個會攔截請求的攔截器。

如下:

image-20220713114037028.png

也就是說

image-20220713101127057.png

這個lookupHandler返回了一個包裝了原有處理器ControllerDemo3和攔截/demo3的攔截器的一個HandlerExecutionChain對象。

整體獲取Handler的流程圖如下:

Spring MVC 流程圖.jpg

第三步:獲取Adapter

核心代碼如下:

// Determine handler adapter for the current request.
   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        //匹配適配器
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
   }

這里this.handlerAdapters與上一步中的this.handlerMappings有一定的對應(yīng)關(guān)系。如下圖:

image-20220713160541229.png

這里匹配到了一個Adapter。

image-20220713161109616.png

此時的HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());返回的ha就是HttpRequestHandlerAdapter

繼續(xù)往下執(zhí)行

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

handler方法:

//這個時候的handler參數(shù)實際是ControllerDemo3
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //將ControllerDemo3強轉(zhuǎn)為父類型,調(diào)用handleRequest方法,實際調(diào)用的就是ControllerDemo3的handleRequest方法
        ((HttpRequestHandler) handler).handleRequest(request, response);
        return null;
    }

ControllerDemo3就是實現(xiàn)了HttpRequestHandler接口,代碼如下:

@Component("/demo3")
public class ControllerDemo3 implements HttpRequestHandler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        WebApplicationContext webApplicationContext = RequestContextUtils.findWebApplicationContext(request);
        System.out.println("===實現(xiàn)HttpRequestHandler接口===");
    }
}

后續(xù)的流程這里不在分析了,至此Spring MVC的核心流程分析完畢。

三、HandlerMapping

public interface HandlerMapping {
    獲取HandlerExecutionChain
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

一開始我們用了4中方式去實現(xiàn)一個自己的Controller。這4中Controller在spring啟動的時候,會被分門別類的去處理。寫法不同,處理方式肯定不同。雖然處理凡是不同,但是結(jié)果又都是相同的。所以這里抽象出一個接口,多個實現(xiàn)去在不通情況下獲取HandlerExecutionChain的。

image-20220713162848151.png

這里以RequestMappinghandlerMapping為例進行分析

image-20220713163423576.png

這里通過查找了解到,AbstractHandlerMethodMapping實現(xiàn)了HandlerMapping接口

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);

        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

        return executionChain;
}

getHandlerInternal的實現(xiàn)如下,中間省略了部分代碼:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        //   ①當(dāng)前這種handler的核心獲取方式是在
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            // ② 這里會在初始化后的mappingRegistry.getMappings()方法里找到匹配的自定義controller
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (!matches.isEmpty()) {
            //  ③獲取match
            Match bestMatch = matches.get(0);
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            handleMatch(bestMatch.mapping, lookupPath, request);
            //  ④這里bestMatch.handlerMethod方法返回的就是UserController對象
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }
image-20220713170031936.png

返回的UserController對象又回被封裝成HandlerExecutionChain對象。執(zhí)行和前面分析的邏輯一樣。

四、這里在分析HandlerAdapter接口

public interface HandlerAdapter {
    boolean supports(Object handler);
    
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    long getLastModified(HttpServletRequest request, Object handler);

handlerAdapter的實現(xiàn):

image-20220714092224604.png

這里針對不通寫法的controller,對應(yīng)不通的xxxAdapter適配器。這里我們以RequestMappingHanderAdapter為例分析一下,這個也是適配如下的controller

@RestController
public class UserController {
    @GetMapping("/hello")
    public String sayHello() {
        return "hello";
    }
}

至于為什么處理的是這種controller,我們可以看下HandlerAdapter接口的supports實現(xiàn),也就是RequestMappingHanderAdapter實現(xiàn)類中的supports方法即可知道原因。

    @Override
    public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }

    @Override
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }

這里我們只需關(guān)注handler instanceof HandlerMethod 這里返回的true,所以也就是RequestMappingHanderAdapter來處理UserContoller了。這里我們也看到了UserContoller類也沒有實現(xiàn)任何抽象,那么UserContoller又如何是HandlerMethod類型呢,后面接著說。這里RequestMappingHanderAdapter最后也是通過反射調(diào)到了實際的controller#sayHello()方法,反射在這里就不過多贅述了。其他的HandlerAdapter實現(xiàn)通過同樣的方式去分析也就清楚了這里為什么要有適配器。

五、Controller和HandlerMethod

RequestMappingHandlerMapping實現(xiàn)了InitializingBean接口,在afterPropertiesSet()方法了,完成UserController到HandlerMethod類型的轉(zhuǎn)變。

    public void afterPropertiesSet() {
        ......
        super.afterPropertiesSet();
    }

父類方法:

    public void afterPropertiesSet() {
        initHandlerMethods();
    }

    protected void initHandlerMethods() {
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                //重點在這里
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void processCandidateBean(String beanName) {
       ......
        if (beanType != null && isHandler(beanType)) {
            //進入這里
            detectHandlerMethods(beanName);
        }
    }

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

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            // 看圖3就很清楚當(dāng)前代碼的作用了。不過我們的關(guān)注點在第四步
            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);
                        }
                    });
            
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                //看這里 第四步
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
image-20220714102418057.png

圖3:

圖3

接著看registerHandlerMethod(handler, invocableMethod, mapping);

跟下去,會跟到如下代碼

AbstractHandlerMethodMapping.java
    

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

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //                                      重點                                                                 //
    //                                      重點                                                                 //
    //                                      重點                                                                 //
    //                                      重點                                                                 //
    //                                      重點                                                                 //
    //這個方法就是把handler封裝一下,最后返回HandlerMethod,這里最后也就說明了為什么我們自定義的Controller的類型是HandlerMethod了//
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected HandlerMethod createHandlerMethod(Object handler, Method method) {
        if (handler instanceof String) {
            //看這里,明白了一切
            return new HandlerMethod((String) handler,
                    obtainApplicationContext().getAutowireCapableBeanFactory(), method);
        }
        return new HandlerMethod(handler, method);
    }

這里在提一個問題,為什么要把Controller封裝成HandlerMethod?這里是因為controller的方法有很多,如果不用HandlerMethod這個通用的類去封裝的話,一些其他功能實現(xiàn)起來可能就比較麻煩了。比如HandlerInterceptor接口,比如我們實現(xiàn)了這么一個接口

@Component
public class MyInterceptor extends HandlerInterceptorAdapter {

    //如果這里不使用handler,傳入的事controller,那么到了調(diào)用父類的super.preHandle方法就沒法適配其他controller的方法了
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("handler is executed!!!!!!!");
        return super.preHandle(request, response, handler);
    }
   ......
}

看下HandlerMethod的注釋

/**
 * Encapsulates information about a handler method consisting of a
 * {@linkplain #getMethod() method} and a {@linkplain #getBean() bean}.
 * Provides convenient access to method parameters, the method return value,
 * method annotations, etc.
 *
 * <p>The class may be created with a bean instance or with a bean name
 * (e.g. lazy-init bean, prototype bean). Use {@link #createWithResolvedBean()}
 * to obtain a {@code HandlerMethod} instance with a bean instance resolved
 * through the associated {@link BeanFactory}.
 */
public class HandlerMethod {}

大意就是:

HandlerMethod封裝了很多屬性,在訪問請求方法的時候可以方便的訪問到方法、方法參數(shù)、方法上的注解、所屬類等并且對方法參數(shù)封裝處理,也可以方便的訪問到方法參數(shù)的注解等信息

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

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

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