Spring 源码

SpringMVC源码关于HandlerMapping

2018-09-06  本文已影响31人  代码potty

从HandlerMapping的注册与初始化问题开始讲
因为是初学,我们根据springMVC的配置文件层层往下
spring-web.xml的代码

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
    
    
        <!-- 开启注解 -->
        <!-- springmvc使用<mvc:annotation-driven> -->
        <!-- 自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter, -->
        <!-- 可用在springmvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。 -->
        <mvc:annotation-driven />
    
        <mvc:default-servlet-handler />
    
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
            <property name="suffix" value=".jsp" />
        </bean>
        
        <context:component-scan base-package="com.alipay.web" />
    </beans>

在这个配置文件中有四个部分
1、<mvc:annotation-driven />
开启springMVC的注解模式

2、<mvc:default-servlet-handler />
REST风格的资源URL不希望带 .html 或 .do 等后缀,为了将静态资源的请求转由Web容器处理,使用这个方法。对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。也就是静态资源处理。

3、配置页面的前置路径和后缀

4、扫描项目中的web包

而HandlerMapping的注册则跟第一个部分<mvc:annotation-driven />相关

Spring是怎么解析<mvc:annotation-driven/>标签的?
首先,必须要有一个继承自“org.springframework.beans.factory.xml.NamespaceHandlerSupport”的类,在其init方法中,注册自己的解析器,注册mvc解析器的类为MvcNamespaceHandler。这个类源码如下:

    public class MvcNamespaceHandler extends NamespaceHandlerSupport {
        public MvcNamespaceHandler() {
        }
    
        public void init() {
            this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
            this.registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
            this.registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
            this.registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
            this.registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
            this.registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
            this.registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
            this.registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
            this.registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
            this.registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
            this.registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
            this.registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
            this.registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
            this.registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
        }
    }

从初始化方法中可以看到,对于annotation-driven有个解析器AnnotationDrivenBeanDefinitionParser。而这个解析器必须实现org.springframework.beans.factory.xml.BeanDefinitionParser接口。
这个类一开始便设置了两个常量

    public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();
    public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();

这两个常量分别是获取HandlerMapping和HandlerAdapter的类名

解析器的主要代码如下:

    parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
    parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
    parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
    parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
    parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
    parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
    //格式转换处理拦截类,比如时间、数字等
    parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));

    // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
    MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

主要目的是注册与mvc处理有关的相关beans以及默认的mvc组件

这边介绍一下HandlerMapping的

     //生成RequestMappingHandlerMapping组件对象
            RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
            handlerMappingDef.setSource(source);
            handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            //优先级设置为最高
            handlerMappingDef.getPropertyValues().add("order", 0);
    //添加contentNegotiationManager属性,处理media type        handlerMappingDef.getPropertyValues().add("contentNegotiationManager",      contentNegotiationManager);
            
            //查看mvc:annotation-driven有无enable-matrix-variables/enableMatrixVariables,表示是否开启多变量映射比如/cars;a=1;b=1
            //具体使用可查阅相关文档,默认removeSemicolonContent为false
            if (element.hasAttribute("enable-matrix-variables")) {
                Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
                handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
            }
            else if (element.hasAttribute("enableMatrixVariables")) {
                Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
                handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
            }
            //配置路径匹配解析器等属性
            configurePathMatchingProperties(handlerMappingDef, element, parserContext);
    //将RequestMappingHandlerMapping注册为bean对象放置bean工厂中       readerContext.getRegistry().registerBeanDefinition(     HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);

RequestMappingHandlerMapping主要是处理@Controller和@RequestMapping注解的,这个是<mvc:annotation-driven />标签默认的配置的,从代码可以看出来,这个标签同时配置了RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 两个bean。

configurePathMatchingProperties配置路径匹配解析器用的,代码如下:

    private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, Element element, ParserContext parserContext) {
    Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching");
    if (pathMatchingElement != null) {
        Object source = parserContext.extractSource(element);
        Boolean useRegisteredSuffixPatternMatch;
        if (pathMatchingElement.hasAttribute("suffix-pattern")) {
            useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern"));
            handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useRegisteredSuffixPatternMatch);
        }

        if (pathMatchingElement.hasAttribute("trailing-slash")) {
            useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("trailing-slash"));
            handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useRegisteredSuffixPatternMatch);
        }

        if (pathMatchingElement.hasAttribute("registered-suffixes-only")) {
            useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only"));
            handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch);
        }

        RuntimeBeanReference pathHelperRef = null;
        if (pathMatchingElement.hasAttribute("path-helper")) {
            pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper"));
        }

        pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, parserContext, source);
        handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef);
        RuntimeBeanReference pathMatcherRef = null;
        if (pathMatchingElement.hasAttribute("path-matcher")) {
            pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher"));
        }

        pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, parserContext, source);
        handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
    }

}

看代码逻辑,主要是通过mvc:annotation-driven下的mvc:path-matching节点来配置

1、suffix-pattern-是否启用后缀匹配,默认为true,即对指定的url会再新增.,比如/user实际匹配/user.->/user匹配/user.html,/user.jsp
2、trailing-slash-是否启动尾部斜线匹配,默认为true,即对指定的url会新增/,比如/user也会匹配/user/
3、registered-suffixes-only-是否启用media type类型的匹配,即对指定的url新增.json/.xml等匹配,默认为false
4、path-helper-路径获取帮助类,默认为UrlPathHelper类
5、path-matcher-路径匹配解析器,默认为AntPathMather

此处完成了对于handlerMapping的注册以后,程序正常走,首先进入到DispatcherServlet进行初始化操作

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

其中的方法initHandlerMappings(context)的代码如下:

    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<HandlerMapping>(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.
                }
            }
    
            // Ensure we have at least one HandlerMapping, by registering
            // a default HandlerMapping if no other mappings are found.
            if (this.handlerMappings == null) {
                this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
                if (logger.isDebugEnabled()) {
                    logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
                }
            }
        }       

语句:detectAllHandlerMappings会判断是否默认添加所有的HandlerMappings,如果存在进行下一步,取出上文注册好的handlerMapping,如果不是,就从配置文件中搜索是否存在相关的bean进行初始化。当两种情况下都未能找到handlerMapping的时候,程序会通过getDefaultStrategies()方法给handlerMappings赋值一个默认的值,这个默认的值在DispatcherServlet.properties中,这个配置文件如下:

    # Default implementation classes for DispatcherServlet's strategy interfaces.
    # Used as fallback when no matching beans are found in the DispatcherServlet context.
    # Not meant to be customized by application developers.
    
    org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
    
    org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
    
    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
        org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
    
    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
        org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
        org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
    
    org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
        org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
        org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
    
    org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
    
    org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
    
    org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

到此完成了对于handlerMapping的初始化工作。

总结
1、通过spring-web.xml配置文件写入<mvc:annotation-driven />标签对mvc需要的组件进行注册,其中重要的部分有handlerMapping、handlerAdapter、interceptor、viewResolver等的注册。注册的类为MvcNamespaceHandler类,然后通过AnnotationDrivenBeanDefinitionParser类的parse()方法去解析标签。

2、组件注册完后,进入DispatcherServlet类进行第二步,组件的初始化,这边介绍handlerMapping的初始化,判断是否默认添加所有的HandlerMappings,如果是,则取出所有注册好的HandlerMapping,否则去context里边找是否有相关的bean,然后取出来,如果前两步都无法初始化handlerMapping,那么程序会给handlerMapping初始化一个默认的值。

3、初始化完成后,调用doDispatch()方法进行操作,首先通过handlerMapping.getHandler(request)的方法获取到handlerExcutionChain对象,然后通过getHandlerAdapter()获取handlerAdapter,传入handlerExcutionChain.getHandler()作为参数,之后调用handlerAdapter.handler()的方法获取ModelAndView,(包括模型数据、逻辑视图名),接着调用applyDefaultViewName(processedRequest, mv)方法获取view,再调用applyPostHandle(request,response,mv)方法,执行HandlerExecutionChain所有Interceptor中的postHandle方法,从这块代码看来,postHandle的执行时机是在执行完方法后没有把响应写入到response中执行的;最后调用 processDispatchResult(),处理请求,把参数都写入到request里面,进行渲染工作。

参考链接:
https://blog.csdn.net/sjjsh2/article/details/53100728
https://www.cnblogs.com/question-sky/p/7090875.html
https://blog.csdn.net/qq_38410730/article/details/79507465
https://blog.csdn.net/wangbiao007/article/details/50510274
https://www.cnblogs.com/davidwang456/p/4132215.html
https://www.cnblogs.com/he-px/p/7133240.html
https://blog.csdn.net/king_is_everyone/article/details/51446260

上一篇 下一篇

猜你喜欢

热点阅读