美文网首页
SpringMVC源码浅析(一):DispatcherServl

SpringMVC源码浅析(一):DispatcherServl

作者: JBryan | 来源:发表于2020-04-04 14:32 被阅读0次

    1、DispatcherServlet的初始化过程概述

    DispatcherServlet是一个Servlet,也具有Servlet的生命周期:实例化(instance)->初始化(init)->调用service方法->销毁(destroy)。当应用启动的时候,会调用DispatcherServlet的初始化方法。本篇文章将浅析DispatcherServlet以及两个重要组件HandlerMapping和HandlerAdapter的初始化过程。


    DispatcherServlet初始化.jpg

    如图所示:
    1、首先会从GenericServlet的init()方法开始,逐步执行到DispatcherServlet的父类FrameworkServlet的initServletBean()方法。
    2、在initServletBean()方法中,会去创建IOC容器WebApplicationContext。
    3、IOC容器会去创建组件,这一步中,重点关注RequestMappingHandlerMapping和RequestMappingHandlerAdapter的初始化。
    4、容器创建完成之后,容器发布ContextRefreshedEvent事件。而FrameworkServlet的内部类ContextRefreshListener实现了ApplicationListener,监听到此事件之后,执行onRefresh()方法
    5、DispatcherServlet重写了父类的onRefresh()方法,并在onRefresh()方法里面,执行初始化策略,从而初始化各个组件。

    2、源码浅析

    断点打到HttpServletBean的init()方法中。

    2.1、DispatcherServlet祖传的init()方法

    HttpServletBean的init()方法:

    public final void init() throws ServletException {
            // 省略其他代码
            // 解析init-param并封装到pvs中。
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            if (!pvs.isEmpty()) {
                try {
                    //将当前的servlet类包装为BeanWrapper,从而注入init-param
                    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;
                }
            }
            // 留给子类扩展
            initServletBean();
            //省略其他代码
        }
    
    

    HttpServletBean的子类FrameworkServlet的initServletBean()方法:

    protected final void initServletBean() throws ServletException {
            //省略其他代码
            long startTime = System.currentTimeMillis();
    
            try {
                //初始化IOC容器WebApplicationContext
                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;
            }
            //省略其他代码
    }
    
    2.2、WebApplicationContext的初始化

    接着上一步中FrameworkServlet的 initWebApplicationContext()方法:

    protected WebApplicationContext initWebApplicationContext() {
            WebApplicationContext rootContext =
                    WebApplicationContextUtils.getWebApplicationContext(getServletContext());
            WebApplicationContext wac = null;
    
            //省略其他代码
            if (wac == null) {
                // 创建一个WebApplicationContext容器
                wac = createWebApplicationContext(rootContext);
            }
            //省略其他代码
            return wac;
        }
    
    

    createWebApplicationContext()方法:

    protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
            Class<?> contextClass = getContextClass();
            //省略其他代码
            ConfigurableWebApplicationContext wac =
                    (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
            //设置环境和父容器
            wac.setEnvironment(getEnvironment());
            wac.setParent(parent);
            String configLocation = getContextConfigLocation();
            if (configLocation != null) {
                wac.setConfigLocation(configLocation);
            }
            //初始化Spring环境包括加载配置文件等
            configureAndRefreshWebApplicationContext(wac);
    
            return wac;
        }
    
    

    configureAndRefreshWebApplicationContext(wac)方法:

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
            //省略其他代码
            // 加载配置文件然后刷新容器
            ConfigurableEnvironment env = wac.getEnvironment();
            if (env instanceof ConfigurableWebEnvironment) {
                ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
            }
    
            postProcessWebApplicationContext(wac);
            applyInitializers(wac);
            wac.refresh();
        }
    
    

    2.3、RequestMappingHandlerMapping的初始化

    RequestMappingHandlerMapping将会实例化,带有@Controller注解的类中@RequestMapping注解的方法。SpringMVC也是基于这个类,找到请求路径映射的类和方法。
    RequestMappingHandlerMapping初始化过程如图所示:


    RequestMappingHandlerMapping.jpg

    因为这个类的爷爷类实现了InitializingBean接口,而他又重写了afterPropertiesSet()方法。因此在这个类初始化的时候,会调用afterPropertiesSet()方法。
    断点打到RequestMappingHandlerMapping类中的afterPropertiesSet()方法。
    afterPropertiesSet方法:

    public void afterPropertiesSet() {
            this.config = new RequestMappingInfo.BuilderConfiguration();
            this.config.setUrlPathHelper(getUrlPathHelper());
            this.config.setPathMatcher(getPathMatcher());
            this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
            this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
            this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
            this.config.setContentNegotiationManager(getContentNegotiationManager());
    
            super.afterPropertiesSet();
        }
    

    然后来到爷爷类的afterPropertiesSet方法:

    @Override
        public void afterPropertiesSet() {
            initHandlerMethods();
        }
    
        /**
         * Scan beans in the ApplicationContext, detect and register handler methods.
         * @see #isHandler(Class)
         * @see #getMappingForMethod(Method, Class)
         * @see #handlerMethodsInitialized(Map)
         */
        protected void initHandlerMethods() {
            if (logger.isDebugEnabled()) {
                logger.debug("Looking for request mappings in application context: " + getApplicationContext());
            }
            //这里的beanNames就是IOC容器中所有的组件名称
            String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
                    obtainApplicationContext().getBeanNamesForType(Object.class));
    
            for (String beanName : beanNames) {
                if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                    Class<?> beanType = null;
                    try {
                        beanType = obtainApplicationContext().getType(beanName);
                    }
                    catch (Throwable ex) {
                        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                        if (logger.isDebugEnabled()) {
                            logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                        }
                    }
                    //判断这个类是否带有@Controller或@RequestMapping注解
                    if (beanType != null && isHandler(beanType)) {
                        //检测这个类的方法
                        detectHandlerMethods(beanName);
                    }
                }
            }
            handlerMethodsInitialized(getHandlerMethods());
        }
    

    isHandler(beanType)方法:

    @Override
        protected boolean isHandler(Class<?> beanType) {
            return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                    AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
        }
    

    detectHandlerMethods(beanName)方法,这里的Handler封装了类的信息,Method类封装了方法的信息,泛型T实际上是RequestMappingInfo,而RequestMappingInfo封装了@RequestMapping注解里面各个属性的值。

    protected void detectHandlerMethods(final Object handler) {
            //首先获取这个类的Class对象
            Class<?> handlerType = (handler instanceof String ?
                    obtainApplicationContext().getType((String) handler) : handler.getClass());
    
            if (handlerType != null) {
                final Class<?> userType = ClassUtils.getUserClass(handlerType);
                //查找这个类的所有方法,以及创建方法的RequestMappingInfo
                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.isDebugEnabled()) {
                    logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
                }
                for (Map.Entry<Method, T> entry : methods.entrySet()) {
                    Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
                    T mapping = entry.getValue();
                    //注册类、方法以及RequestMappingInfo信息
                    registerHandlerMethod(handler, invocableMethod, mapping);
                }
            }
        }
    
    

    selectMethods()方法:

    public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
            //用于返回的Map,key是方法对象,value是封装的RequestInfo对象
            final Map<Method, T> methodMap = new LinkedHashMap<>();
            Set<Class<?>> handlerTypes = new LinkedHashSet<>();
            Class<?> specificHandlerType = null;
    
            if (!Proxy.isProxyClass(targetType)) {
                handlerTypes.add(targetType);
                specificHandlerType = targetType;
            }
            handlerTypes.addAll(Arrays.asList(targetType.getInterfaces()));
    
            for (Class<?> currentHandlerType : handlerTypes) {
                final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
                
                //使用反射处理这个类的所有方法
                ReflectionUtils.doWithMethods(currentHandlerType, method -> {
                    Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                    T result = metadataLookup.inspect(specificMethod);
                    if (result != null) {
                        Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                        if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
                            methodMap.put(specificMethod, result);
                        }
                    }
                }, ReflectionUtils.USER_DECLARED_METHODS);
            }
    
            return methodMap;
        }
    
    

    doWithMethods()方法:

    public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
            // Keep backing up the inheritance hierarchy.
            Method[] methods = getDeclaredMethods(clazz);
            //遍历这个类的所有方法
            for (Method method : methods) {
                if (mf != null && !mf.matches(method)) {
                    continue;
                }
                try {
                    //执行的是上一步中lambda表达式中的方法
                    mc.doWith(method);
                }
                catch (IllegalAccessException ex) {
                    throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
                }
            }
            if (clazz.getSuperclass() != null) {
            //然后再递归调用父类的所有方法,这里直接进入Object类的所有方法。
                doWithMethods(clazz.getSuperclass(), mc, mf);
            }
            else if (clazz.isInterface()) {
                for (Class<?> superIfc : clazz.getInterfaces()) {
                    doWithMethods(superIfc, mc, mf);
                }
            }
        }
    
    

    mc.doWith(method);

    method -> {
                    Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                    //检查这个方法,调用的是detectHandlerMethods()里面的lambda表达式中的方法
                   //返回的是一个RequestMappingInfo对象
                    T result = metadataLookup.inspect(specificMethod);
                    if (result != null) {
                        Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                        if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
                            //将这个键值对放进map里面
                            methodMap.put(specificMethod, result);
                        }
                    }
                }
    

    metadataLookup.inspect(specificMethod);

    (MethodIntrospector.MetadataLookup<T>) method -> {
                            try {
                                return getMappingForMethod(method, userType);
                            }
                            catch (Throwable ex) {
                                throw new IllegalStateException("Invalid mapping on handler class [" +
                                        userType.getName() + "]: " + method, ex);
                            }
                        }
    

    getMappingForMethod(method, userType)方法:

    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
            //首先创建这个方法的RequestMappingInfo对象
            RequestMappingInfo info = createRequestMappingInfo(method);
            if (info != null) {
                //然后创建这个类的RequestMappingInfo对象
                RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
                if (typeInfo != null) {
                    //将这两个RequestMappingInfo对象合并为一个对象,拼接类和方法上的请求路径等等。
                    info = typeInfo.combine(info);
                }
            }
            return info;
        }
    

    typeInfo.combine(info)方法

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

    重点来了,经过上面的步骤,已经将需要注册的类和方法放到Map<Method, T>中了。接下来,就是遍历这个Map,然后注册Method和RequestMappingInfo了。
    在detectHandlerMethods()方法的最后:

    for (Map.Entry<Method, T> entry : methods.entrySet()) {
                    Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
                    T mapping = entry.getValue();
                    registerHandlerMethod(handler, invocableMethod, mapping);
                }
    

    然后进入registerHandlerMethod()方法:

    public void register(T mapping, Object handler, Method method) {
                //加上写锁
                this.readWriteLock.writeLock().lock();
                try {
                    //创建HandlerMethod对象
                    //HandlerMethod封装了类、方法、方法参数数组、这个类的beanFactory等信息。
                    HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                   //断言创建出来的HandlerMethod对象是唯一的
                    assertUniqueMethodMapping(handlerMethod, mapping);
    
                    if (logger.isInfoEnabled()) {
                        logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
                    }
                    //RequestMappingInfo作为键,handlerMethod作为值,放入mappingLookup中。
                    this.mappingLookup.put(mapping, handlerMethod);
                    //获取RequestMappingInfo的请求路径url
                    List<String> directUrls = getDirectUrls(mapping);
                    for (String url : directUrls) {
                        //将url作为键,RequestMappingInfo作为值,放入urlLookup中。
                        this.urlLookup.add(url, mapping);
                    }
                    //生成一个名字name
                    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);
                    }
                    //把以上所有的信息,放进registry里面
                    this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
                }
                finally {
                    this.readWriteLock.writeLock().unlock();
                }
            }
    

    DispatcherServlet拿到一个请求路径时,就是根据这一步中的几个属性,来选择处理这个请求的类和方法的,到此,RequestMappingHandlerMapping初始化结束。

    2.4、RequestMappingHandlerAdapter的初始化

    RequestMappingHandlerAdapter实现了InitializingBean接口,因此会在初始化时,调用afterPropertiesSet()方法:

    public void afterPropertiesSet() {
            // 首先初始化ResponseBody增强的bean,赋值给argumentResolvers属性
            initControllerAdviceCache();
            //初始化各种参数解析器
            if (this.argumentResolvers == null) {
                List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
                this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
            }
            //初始化@initBind参数解析器,赋值给initBinderArgumentResolvers 属性
            if (this.initBinderArgumentResolvers == null) {
                List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
                this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
            }
            //初始化返回值的解析器,赋值给returnValueHandlers 属性
            if (this.returnValueHandlers == null) {
                List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
                this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
            }
        }
    
    2.5、DispatcherServlet的初始化方法

    DispatcherServlet的父类FrameworkServlet里面有个内部类ContextRefreshListener,ContextRefreshListener实现了ApplicationListener接口。

    private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
    
            @Override
            public void onApplicationEvent(ContextRefreshedEvent event) {
                FrameworkServlet.this.onApplicationEvent(event);
            }
        }
    

    IOC容器创建完成以后,会发布ContextRefreshedEvent事件,ContextRefreshListener监听到此事件以后,进入onApplicationEvent()方法:

    public void onApplicationEvent(ContextRefreshedEvent event) {
            this.refreshEventReceived = true;
            onRefresh(event.getApplicationContext());
        }
    

    FrameworkServlet的onRefresh()是个空实现,而子类DispatcherServlet重写了这个方法,initStrategies()方法内部,实现了DispatcherServlet的初始化逻辑

    @Override
        protected void onRefresh(ApplicationContext context) {
            initStrategies(context);
        }
    
        /**
         * Initialize the strategy objects that this servlet uses.
         * <p>May be overridden in subclasses in order to initialize further strategy objects.
         */
        protected void initStrategies(ApplicationContext context) {
            //MultipartResolver用于处理文件上传
            initMultipartResolver(context);
            //Spring的国际化配置
            initLocaleResolver(context);
            //Web用Theme来控制网页风格,一个主题就是一组静态资源
            initThemeResolver(context);
            //客户端发出Request时,DispatcherServlet将Request提交给HandlerMapping
            //然后HandlerMapping返回响应的Controller
            initHandlerMappings(context);
            //HandlerAdapter用于处理Http请求
            initHandlerAdapters(context);
            //异常处理解析器
            initHandlerExceptionResolvers(context);
            //没有返回视图名称的时候,如何确定视图名称的组件
            initRequestToViewNameTranslator(context);
            //根据ModelAndView选择合适的View进行渲染
            initViewResolvers(context);
            //Flash attributes在重定向之前暂存,以便重定向之后还能使用
            //FlashMap用于保持Flash attributes
            initFlashMapManager(context);
        }
    

    其中initHandlerMappings()方法:

    private void initHandlerMappings(ApplicationContext context) {
            this.handlerMappings = null;
    
            if (this.detectAllHandlerMappings) {
                // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
                //找到ApplicationContext及其父context中的HandlerMappings
                Map<String, HandlerMapping> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
                if (!matchingBeans.isEmpty()) {
                    //赋值给handlerMappings
                    this.handlerMappings = new ArrayList<>(matchingBeans.values());
                    // We keep HandlerMappings in sorted order.
                    AnnotationAwareOrderComparator.sort(this.handlerMappings);
                }
            }
            //省略其他代码
        }
    

    DispatcherServlet在选用HandlerMapping的过程中,将根据我们所指定的一系列HandlerMapping的优先级进行排序,然后优先使用优先级在前的HandlerMapping,如果当前的HandlerMapping能够返回可用的Handler,DispatcherServlet则使用当前返回的Handler进行Web请求的处理,而不再继续询问其他的HandlerMapping。否则,DispatcherServlet将继续按照各个HandlerMapping的优先级进行询问,直到获取一个可用的Handler为止。

    排序后的handlerMapping如图,前面分析的RequestMappingHandlerMapping被放在了集合的第一位。


    handlerMapping.jpg

    initHandlerAdapters()方法:

    private void initHandlerAdapters(ApplicationContext context) {
            this.handlerAdapters = null;
    
            if (this.detectAllHandlerAdapters) {
                // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
                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.
                    AnnotationAwareOrderComparator.sort(this.handlerAdapters);
                }
            }
            //省略其他代码
        }
    
    

    DispatcherServlet通过HandlerMapping得到Handler后,会轮询HandlerAdapter模块,查找能够处理当前HTTP请求的HandlerAdapter实现,HandlerAdapter模块根据Handler类型,来选择某个HandlerAdapter,从而适配当前的HTTP请求。
    排序后的HandlerAdapter:


    Handler Adapter.jpg

    同样前面分析的RequestMappingHandlerAdapter在集合的第一位。其他的初始化方法与initHandlerMapping和initHandlerAdapter类似,就不再累述了。

    相关文章

      网友评论

          本文标题:SpringMVC源码浅析(一):DispatcherServl

          本文链接:https://www.haomeiwen.com/subject/jwexphtx.html