Spring MVC源碼分析之請求分發(fā)

前言

在使用SpringMvc時坑傅,通過ControllerRequestMapping,就能實現(xiàn)網(wǎng)絡(luò)請求的處理疮方。那么控嗜,這是怎么實現(xiàn)的呢?請求是如何從Tomcat進(jìn)入到controller里的方法的呢骡显?

核心流程概覽

宏觀上看疆栏,流程如下:

  1. 創(chuàng)建DispatcherServlet實例
  2. 創(chuàng)建Tomcat實例
  3. 通過ServletContainerInitializer以及ServletContextInitializerDispatcherServlet注冊到TomcatServlet容器里
  4. HandlerMapping綁定到DispatcherServlet
  5. 請求通過Tomcat進(jìn)到DispatcherServlet
  6. DispatcherServlet根據(jù)request pathHandlerMapping查找請求處理方法

源碼分析

1. 注冊controller

Spring在初始化RequestMappingHandlerMapping這個Bean時會將Controller層帶有RequestMapping等相關(guān)注解的方法跟注解信息的PATH分別作為key value注冊到RequestMappingHandlerMapping中。然后RequestMappingHandlerMapping會在第一次請求到來時被注冊到DispatcherServlet

1.1 初始化RequestMappingHandlerMapping

RequestMappingHandlerMapping 繼承了InitializingBean蟆盐,在Spring創(chuàng)建RequestMappingHandlerMapping的實例時會去調(diào)用其afterPropertiesSet方法進(jìn)行初始化

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {

        protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
                    throws Throwable {
                //判斷當(dāng)前bean是否繼承了InitializingBean
                boolean isInitializingBean = (bean instanceof InitializingBean);
        
                if (isInitializingBean) {
                    //初始化
                    ((InitializingBean) bean).afterPropertiesSet();
                }
            }
}

1.2 遍歷Controller

RequestMappingHandlerMapping會把所有的Spring Bean對應(yīng)的類里有Controller或者RequestMapping注解的類拿出來處理

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

        protected void processCandidateBean(String beanName) {
                Class<?> beanType = null;
                try {
                    beanType = obtainApplicationContext().getType(beanName);
                }
                catch (Throwable ex) {
                    ...
                }
                //只處理有相應(yīng)注解的bean
                if (beanType != null && isHandler(beanType)) {
                    detectHandlerMethods(beanName);
                }
            }
        
        //判斷是否有相應(yīng)注解
        protected boolean isHandler(Class<?> beanType) {
                return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                        AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
            }
}

1.3 生成RequestMappingInfo

把1.2里得到的類里的所有帶有RequestMapping注解的方法拿出來包裝成RequestMappingInfo 并塞到RequestMappingHandlerMapping內(nèi)部的hashMap

public final class MethodIntrospector {

        public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
                final Map<Method, T> methodMap = new LinkedHashMap<>();
                Set<Class<?>> handlerTypes = new LinkedHashSet<>();
                Class<?> specificHandlerType = null;
        
                handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
        
                for (Class<?> currentHandlerType : handlerTypes) {
                    final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
        
                    //通過反射找到targetType下的所有method
                    ReflectionUtils.doWithMethods(currentHandlerType, method -> {
                        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                        //判斷method上是否有@RequestMapping承边,沒有則返回null
                        T result = metadataLookup.inspect(specificMethod);
                        if (result != null) {
                            //如果是橋接方法,就返回被橋接的方法石挂。否則返回specificMethod
                            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                            //如果specificMethod是橋接方法博助,則不添加到methodMap中。否則同一個方法就會被添加兩次
                            if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
                                methodMap.put(specificMethod, result);
                            }
                        }
                    }, ReflectionUtils.USER_DECLARED_METHODS);
                }
        
                return methodMap;
            }
}

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

        // metadataLookup.inspect(specificMethod)是MetadataLookup#inspect的一個匿名實現(xiàn)
        protected void detectHandlerMethods(Object handler) {
                ...
        
                Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                            (MethodIntrospector.MetadataLookup<T>) method -> {
                                //根據(jù)方法是否被RequestMapping.class標(biāo)注來判斷方法是否可以被注冊
                                return getMappingForMethod(method, userType);
                            });
        
            methods.forEach((method, mapping) -> {
                        Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                          //準(zhǔn)備注冊
                        registerHandlerMethod(handler, invocableMethod, mapping);
                    });
                ...
            }
}

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {

        //判斷方法是否應(yīng)該被注冊
        private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
                //判斷方法上是否有RequestMapping.class
                RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
                RequestCondition<?> condition = (element instanceof Class ?
                        getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
            //創(chuàng)建RequestMappingInfo
                return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
            }
}

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
        //注冊
        public void register(T mapping, Object handler, Method method) {
                    ...

            //把類名handler跟方法method包裝成一個HandlerMethod對象
                    HandlerMethod handlerMethod = createHandlerMethod(handler, method);
    
                    //把方法注解里的path跟mapping的關(guān)系保存下來痹愚,
                    //后面訪問的時候會先從httpRequest解析出path富岳,再根據(jù)path找到mapping
                    Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
                    for (String path : directPaths) {
                        this.pathLookup.add(path, mapping);
                    }
    
                    //mapping是上面創(chuàng)建的RequestMappingInfo
                    this.registry.put(mapping,
                            new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
            }
}

1.4 注冊HandlerMapping

HandlerMapping注冊到DispatcherServlet蛔糯。在第一次請求時進(jìn)行初始化時觸發(fā)

public class DispatcherServlet extends FrameworkServlet {

        private void initHandlerMappings(ApplicationContext context) {
                this.handlerMappings = null;
        
                if (this.detectAllHandlerMappings) {
                    //從spring上下文里拿到所有實現(xiàn)了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.
                        AnnotationAwareOrderComparator.sort(this.handlerMappings);
                    }
                }
            }
}

1.5 注冊DispatcherServlet

DispatcherServlet 注冊到TomcatServlet容器里

public class ServletWebServerApplicationContext extends GenericWebApplicationContext
        implements ConfigurableWebServerApplicationContext {        
        
        private void createWebServer() {
                ...
                //創(chuàng)建webServer并添加下面的ServletContextInitializer匿名實現(xiàn)
                this.webServer = factory.getWebServer(getSelfInitializer());
                ...
        }

        private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
                return this::selfInitialize;
        }

        private void selfInitialize(ServletContext servletContext) throws ServletException {            
            ...
            //找到所有實現(xiàn)了ServletContextInitializer接口的Spring bean
            //這里拿到的是DispatcherServletRegistrationBean
            //而DispatcherServletRegistrationBean里持有DispatcherServlet的Spring bean
            for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                //將DispatcherServlet注冊到Tomcat的Servlet容器中
                beans.onStartup(servletContext);
            }
        }
}

@Configuration(proxyBeanMethods = false)
protected static class DispatcherServletRegistrationConfiguration {

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
                WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            //通過DispatcherServlet創(chuàng)建DispatcherServletRegistrationBean
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
                    webMvcProperties.getServlet().getPath());
            return registration;
        }
}

public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
        @Override
        public final void onStartup(ServletContext servletContext) throws ServletException {
            //注冊DispatcherServlet。最終會注冊到StandardWrapper#setServlet
            register(description, servletContext);
        }
}

//Tomcat啟動時觸發(fā)
class TomcatStarter implements ServletContainerInitializer {    

    public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
        //這里會拿到上面添加的匿名ServletContextInitializer
        for (ServletContextInitializer initializer : this.initializers) {
                initializer.onStartup(servletContext);
            }
    }
}

2. 訪問controller

2.1 請求進(jìn)入DispatcherServlet

  1. Tomcat從Servlet容器中取出DispatcherServlet窖式,并將請求交給DispatcherServlet
public class DispatcherServlet extends FrameworkServlet {
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
                //根據(jù)request的url跟http method從RequestMappingHandlerMapping.registry中獲取對應(yīng)的
                //請求處理器
                mappedHandler = getHandler(processedRequest);
        
                ...
        
                //調(diào)用請求處理方法      
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            }

        protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
                if (this.handlerMappings != null) {
        
                    //遍歷大步驟1小步驟4時注冊進(jìn)來的handlerMappings
                    for (HandlerMapping mapping : this.handlerMappings) {
        
                        //根據(jù)request path查找handler
                        HandlerExecutionChain handler = mapping.getHandler(request);
                        if (handler != null) {
                            return handler;
                        }
                    }
                }
                return null;
            }
}

2.2 查找請求處理方法

RequestMappingHandlerMapping 根據(jù)HttpServletRequest查找請求處理器

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

        protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
                //解析出url的path
                String lookupPath = initLookupPath(request);
                this.mappingRegistry.acquireReadLock();
                try {
                    //根據(jù)path找到對應(yīng)的HandlerMethod
                    HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
                    return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
                }
                finally {
                    this.mappingRegistry.releaseReadLock();
                }
            }
        
        protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
                List<Match> matches = new ArrayList<>();
        
                //通過lookupPath找到mapping
                List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
                if (directPathMatches != null) {
                    //根據(jù)mapping從RequestMappingHandlerMapping#registry里
                    //找到對應(yīng)的RequestMappingInfo并包裝成Match對象蚁飒,放入matches中
                    addMatchingMappings(directPathMatches, matches, request);
                }
                
                if (!matches.isEmpty()) {
                    Match bestMatch = matches.get(0);
                    
                    //從RequestMappingInfo獲取HandlerMethod
                    return bestMatch.getHandlerMethod();
                }
            }
        
        private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
                for (T mapping : mappings) {
                    T match = getMatchingMapping(mapping, request);
                    if (match != null) {
                        //根據(jù)mapping找到RequestMappingInfo并包裝成Match對象
                        matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
                    }
                }
            }
        
        public Map<T, MappingRegistration<T>> getRegistrations() {
                    //第一步里的registry
                    return this.registry;
                }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市萝喘,隨后出現(xiàn)的幾起案子淮逻,更是在濱河造成了極大的恐慌,老刑警劉巖阁簸,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爬早,死亡現(xiàn)場離奇詭異,居然都是意外死亡启妹,警方通過查閱死者的電腦和手機(jī)筛严,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饶米,“玉大人桨啃,你說我怎么就攤上這事∶适洌” “怎么了照瘾?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長褪猛。 經(jīng)常有香客問我网杆,道長羹饰,這世上最難降的妖魔是什么伊滋? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮队秩,結(jié)果婚禮上笑旺,老公的妹妹穿的比我還像新娘。我一直安慰自己馍资,他們只是感情好筒主,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸟蟹,像睡著了一般乌妙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上建钥,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天藤韵,我揣著相機(jī)與錄音,去河邊找鬼熊经。 笑死泽艘,一個胖子當(dāng)著我的面吹牛欲险,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匹涮,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼天试,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了然低?” 一聲冷哼從身側(cè)響起喜每,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雳攘,沒想到半個月后灼卢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡来农,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年鞋真,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沃于。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡涩咖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出繁莹,到底是詐尸還是另有隱情檩互,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布咨演,位于F島的核電站闸昨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏薄风。R本人自食惡果不足惜饵较,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望遭赂。 院中可真熱鬧循诉,春花似錦、人聲如沸撇他。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽困肩。三九已至划纽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锌畸,已是汗流浹背勇劣。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留蹋绽,地道東北人芭毙。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓筋蓖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親退敦。 傳聞我的和親對象是個殘疾皇子粘咖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容