SpingMVC模块常用几种handlerMapping的初始化过程

本文介绍Spring MVC中HandlerMapping的初始化过程,包括RequestMappingHandlerMapping、SimpleUrlHandlerMapping及BeanNameUrlHandlerMapping三个核心组件的引入与初始化细节。

以xml文件为例,内容如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.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.xsd">
    <!-- 默认的注解映射的支持 --> 
    <context:component-scan base-package="**.**.controller"/>
    <mvc:annotation-driven>
        <mvc:message-converters>
            <ref bean="fastJsonHttpMessageConverter" />
        </mvc:message-converters>
    </mvc:annotation-driven>
    <mvc:default-servlet-handler />
    <!-- 处理JSON数据转换的 -->
    <bean id="fastJsonHttpMessageConverter"
        class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
        <!-- 为了处理返回的JSON数据的编码,默认是ISO-88859-1的,这里把它设置为utf-8,解决有乱码的情况 -->
        <property name="supportedMediaTypes">
            <list>
                <value>application/json;charset=utf-8</value>
            </list>
        </property>
    </bean>
</beans>

这里,主要关心的是HandlerMapping接口实现类的初始化问题,所以主要关注以下两个标签即可。

<mvc:annotation-driven>
<mvc:default-servlet-handler/>

针对mvc的命名空间,spring-webmvc工程中spring.handlers内容如下http://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler

public class MvcNamespaceHandler extends NamespaceHandlerSupport {  

    public void init() {  
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());  
        registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());  
        registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());  
        registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());  
        registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());  
    }  

}  

1、RequestMappingHandlerMapping类的引入与初始化

  • RequestMappingHandlerMapping类引入
<mvc:annotation-driven>

annotation-driven标签,用来支持基于注解的映射,它的实现类是AnnotationDrivenBeanDefinitionParser,这个类又向容器中注册了RequestMappingHandlerMapping,它就是一个HandlerMapping接口实现类。 RequestMappingHandlerMapping是默认的第一个HandlerMapping,因为它实现了order接口,同时赋值为0。

class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
。。。。。。
    public BeanDefinition parse(Element element, ParserContext parserContext) {
    。。。。。。
        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
        handlerMappingDef.setSource(source);
        handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        //RequestMappingHandlerMapping是handler中默认的第一个,因为实现了order接口
        handlerMappingDef.getPropertyValues().add("order", 0);
        handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
        。。。。。。
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
        MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

        parserContext.popAndRegisterContainingComponent();

        return null;
    }

    。。。。。。

}
  • RequestMappingHandlerMapping类初始化
    这里主要是说url和handler之间映射关系的初始化,对应AbstractHandlerMethodMapping类的urlMap属性。RequestMappingHandlerMapping类实现了InitializingBean接口,所以初始化时会调用RequestMappingHandlerMapping类的afterPropertiesSet方法,url和handler之间映射关系的初始化就是这里,是通过扫描特定包内类的注解实现的(主要扫描Controller和RequestMapping注解)。例如,扫描到com.winksi.roinchina.controller包下的RoinChinaController类时,发现getToken方法有注解@RequestMapping,所以就会向urlMap内,添加一条记录,key为/getToken。
/**
 * Interface to be implemented by beans that need to react once all their
 * properties have been set by a BeanFactory: for example, to perform custom
 * initialization, or merely to check that all mandatory properties have been set.
 *
 * <p>An alternative to implementing InitializingBean is specifying a custom
 * init-method, for example in an XML bean definition.
 * For a list of all bean lifecycle methods, see the BeanFactory javadocs.
 *
 * @author Rod Johnson
 * @see BeanNameAware
 * @see BeanFactoryAware
 * @see BeanFactory
 * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
 * @see org.springframework.context.ApplicationContextAware
 */
public interface InitializingBean {

    /**
     * Invoked by a BeanFactory after it has set all bean properties supplied
     * (and satisfied BeanFactoryAware and ApplicationContextAware).
     * <p>This method allows the bean instance to perform initialization only
     * possible when all bean properties have been set and to throw an
     * exception in the event of misconfiguration.
     * @throws Exception in the event of misconfiguration (such
     * as failure to set an essential property) or if initialization fails.
     */
    void afterPropertiesSet() throws Exception;

}
package com.winksi.roinchina.controller;
@Controller
public class RoinChinaController {

    private static final Logger log = LoggerFactory.getLogger(RoinChinaController.class);

    public RoinChinaController(){
        log.info("RoinChinaController init");
    }

    /**
     * 查询归属地v1.0接口
     * 
     * @param input
     * @return
     */
    @RequestMapping(value = "/getToken", method = RequestMethod.POST)
    @ResponseBody
    public JSONObject getToken(@RequestBody JSONObject input) {
        JSONObject json = new JSONObject();
        json.put("akey", "avalue");
        return json;
    }
}

这里写图片描述

2、SimpleUrlHandlerMapping类的引入与初始化

  • SimpleUrlHandlerMapping类的引入
<mvc:default-servlet-handler/>

default-servlet-handler标签的解析,会引入DefaultServletHandlerBeanDefinitionParser解析类。在DefaultServletHandlerBeanDefinitionParser类的parse方法内,会引入SimpleUrlHandlerMapping类。

class DefaultServletHandlerBeanDefinitionParser implements BeanDefinitionParser {

    public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);

        String defaultServletName = element.getAttribute("default-servlet-name");
        RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
        defaultServletHandlerDef.setSource(source);
        defaultServletHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        if (StringUtils.hasText(defaultServletName)) {
            defaultServletHandlerDef.getPropertyValues().add("defaultServletName", defaultServletName);
        }
        String defaultServletHandlerName = parserContext.getReaderContext().generateBeanName(defaultServletHandlerDef);
        parserContext.getRegistry().registerBeanDefinition(defaultServletHandlerName, defaultServletHandlerDef);
        parserContext.registerComponent(new BeanComponentDefinition(defaultServletHandlerDef, defaultServletHandlerName));

        Map<String, String> urlMap = new ManagedMap<String, String>();
        urlMap.put("/**", defaultServletHandlerName);

        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
        handlerMappingDef.setSource(source);
        handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        handlerMappingDef.getPropertyValues().add("urlMap", urlMap);

        String handlerMappingBeanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
        parserContext.getRegistry().registerBeanDefinition(handlerMappingBeanName, handlerMappingDef);
        parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, handlerMappingBeanName));

        /**
         * 里面有RootBeanDefinition beanNameMappingDef = new RootBeanDefinition(BeanNameUrlHandlerMapping.class);类的定义
         */
        // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
        MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
        return null;
    }
}
  • SimpleUrlHandlerMapping类的初始化
    SimpleUrlHandlerMapping实现了ApplicationContextAware接口(定义setApplicationContext方法),所以初始化SimpleUrlHandlerMapping时,会调用SimpleUrlHandlerMapping类的setApplicationContext方法。因为SimpleUrlHandlerMapping没有实现setApplicationContext方法,所以实际调用的是ApplicationObjectSupport类的setApplicationContext方法。具体调用顺序,可以参考下图。
    这里写图片描述
    需要注意SimpleUrlHandlerMapping类的urlMap属性的赋值,这个属性的复制是在DefaultServletHandlerBeanDefinitionParser类注册SimpleUrlHandlerMapping类时实现的。具体代码如下
    Map<String, String> urlMap = new ManagedMap<String, String>();
    urlMap.put("/**", defaultServletHandlerName);

    RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
    handlerMappingDef.setSource(source);
    handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    handlerMappingDef.getPropertyValues().add("urlMap", urlMap);

3、BeanNameUrlHandlerMapping类的引入与初始化
- BeanNameUrlHandlerMapping类的引入

<mvc:annotation-driven>
<mvc:default-servlet-handler/>

上面两个标签的解析,都会涉及到引入BeanNameUrlHandlerMapping类,引入代码如下。这里需要注意,bean的名字或别名,必须是/开头。例如

<bean name="/beanNameUrlController" class="com.zhao.web.BeanNameUrlController"/>
BeanNameUrlHandlerMapping类的引入,AnnotationDrivenBeanDefinitionParser类和DefaultServletHandlerBeanDefinitionParser类都会调用到这段代码.
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

MvcNamespaceUtils类的方法
public static void registerDefaultComponents(ParserContext parserContext, @Nullable Object source) {
        registerBeanNameUrlHandlerMapping(parserContext, source);
        registerHttpRequestHandlerAdapter(parserContext, source);
        registerSimpleControllerHandlerAdapter(parserContext, source);
        registerHandlerMappingIntrospector(parserContext, source);
    }

private static void registerBeanNameUrlHandlerMapping(ParserContext context, @Nullable Object source) {
        if (!context.getRegistry().containsBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME)){
            RootBeanDefinition mappingDef = new RootBeanDefinition(BeanNameUrlHandlerMapping.class);
            mappingDef.setSource(source);
            mappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            mappingDef.getPropertyValues().add("order", 2); // consistent with WebMvcConfigurationSupport
            RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);
            mappingDef.getPropertyValues().add("corsConfigurations", corsRef);
            context.getRegistry().registerBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME, mappingDef);
            context.registerComponent(new BeanComponentDefinition(mappingDef, BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME));
        }
    }
  • BeanNameUrlHandlerMapping 类的初始化
    BeanNameUrlHandlerMapping实现了ApplicationContextAware接口(定义setApplicationContext方法),所以初始化BeanNameUrlHandlerMapping时,会调用SimpleUrlHandlerMapping类的setApplicationContext方法。具体调用顺序,可以参考下图。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值