Spring MVC源码分析之请求分发

前言

在使用SpringMvc时,通过ControllerRequestMapping,就能实现网络请求的处理。那么,这是怎么实现的呢?请求是如何从Tomcat进入到controller里的方法的呢?

核心流程概览

宏观上看,流程如下:

  1. 创建DispatcherServlet实例
  2. 创建Tomcat实例
  3. 通过ServletContainerInitializer以及ServletContextInitializerDispatcherServlet注册到TomcatServlet容器里
  4. HandlerMapping绑定到DispatcherServlet
  5. 请求通过Tomcat进到DispatcherServlet
  6. DispatcherServlet根据request pathHandlerMapping查找请求处理方法

源码分析

1. 注册controller

Spring在初始化RequestMappingHandlerMapping这个Bean时会将Controller层带有RequestMapping等相关注解的方法跟注解信息的PATH分别作为key value注册到RequestMappingHandlerMapping中。然后RequestMappingHandlerMapping会在第一次请求到来时被注册到DispatcherServlet

1.1 初始化RequestMappingHandlerMapping

RequestMappingHandlerMapping 继承了InitializingBean,在Spring创建RequestMappingHandlerMapping的实例时会去调用其afterPropertiesSet方法进行初始化

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {

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

1.2 遍历Controller

RequestMappingHandlerMapping会把所有的Spring Bean对应的类里有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) {
                    ...
                }
                //只处理有相应注解的bean
                if (beanType != null && isHandler(beanType)) {
                    detectHandlerMethods(beanName);
                }
            }
        
        //判断是否有相应注解
        protected boolean isHandler(Class<?> beanType) {
                return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                        AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
            }
}

1.3 生成RequestMappingInfo

把1.2里得到的类里的所有带有RequestMapping注解的方法拿出来包装成RequestMappingInfo 并塞到RequestMappingHandlerMapping内部的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的一个匿名实现
        protected void detectHandlerMethods(Object handler) {
                ...
        
                Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                            (MethodIntrospector.MetadataLookup<T>) method -> {
                                //根据方法是否被RequestMapping.class标注来判断方法是否可以被注册
                                return getMappingForMethod(method, userType);
                            });
        
            methods.forEach((method, mapping) -> {
                        Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                          //准备注册
                        registerHandlerMethod(handler, invocableMethod, mapping);
                    });
                ...
            }
}

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {

        //判断方法是否应该被注册
        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));
            //创建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的关系保存下来,
                    //后面访问的时候会先从httpRequest解析出path,再根据path找到mapping
                    Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
                    for (String path : directPaths) {
                        this.pathLookup.add(path, mapping);
                    }
    
                    //mapping是上面创建的RequestMappingInfo
                    this.registry.put(mapping,
                            new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
            }
}

1.4 注册HandlerMapping

HandlerMapping注册到DispatcherServlet。在第一次请求时进行初始化时触发

public class DispatcherServlet extends FrameworkServlet {

        private void initHandlerMappings(ApplicationContext context) {
                this.handlerMappings = null;
        
                if (this.detectAllHandlerMappings) {
                    //从spring上下文里拿到所有实现了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() {
                ...
                //创建webServer并添加下面的ServletContextInitializer匿名实现
                this.webServer = factory.getWebServer(getSelfInitializer());
                ...
        }

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

        private void selfInitialize(ServletContext servletContext) throws ServletException {            
            ...
            //找到所有实现了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创建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启动时触发
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 请求进入DispatcherServlet

  1. Tomcat从Servlet容器中取出DispatcherServlet,并将请求交给DispatcherServlet
public class DispatcherServlet extends FrameworkServlet {
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
                //根据request的url跟http method从RequestMappingHandlerMapping.registry中获取对应的
                //请求处理器
                mappedHandler = getHandler(processedRequest);
        
                ...
        
                //调用请求处理方法      
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            }

        protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
                if (this.handlerMappings != null) {
        
                    //遍历大步骤1小步骤4时注册进来的handlerMappings
                    for (HandlerMapping mapping : this.handlerMappings) {
        
                        //根据request path查找handler
                        HandlerExecutionChain handler = mapping.getHandler(request);
                        if (handler != null) {
                            return handler;
                        }
                    }
                }
                return null;
            }
}

2.2 查找请求处理方法

RequestMappingHandlerMapping 根据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 {
                    //根据path找到对应的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) {
                    //根据mapping从RequestMappingHandlerMapping#registry里
                    //找到对应的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) {
                        //根据mapping找到RequestMappingInfo并包装成Match对象
                        matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
                    }
                }
            }
        
        public Map<T, MappingRegistration<T>> getRegistrations() {
                    //第一步里的registry
                    return this.registry;
                }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容