spring mvc问题之为何多注入了一个BeanNameUrlHandlerMapping?

来源:互联网 发布:vm虚拟机安装mac 编辑:程序博客网 时间:2024/05/22 03:06

最近在研究springmvc,为了把各种配置方式都了解一下,所以在springmvc的配置文件里组合了几种算是常用的handlermapping和controller组件的配置方式,包括注解配置方式以及xml配置方式,配置文件如下:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:context="http://www.springframework.org/schema/context"    xmlns:mvc="http://www.springframework.org/schema/mvc"    xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.2.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">    <!-- 开启后仍需结合<context:component-scan>使用 -->    <mvc:annotation-driven />    <!-- 使用XML来启用组件扫描 -->    <!-- <context:component-scan/>会到指定的包(package)下面扫描标注有@Component的类,所以Controller要被扫描到,一定要加上@Component注解 -->    <!-- 扫描controller(controller层注入) -->    <!-- 如果需要被springmvc识别为controller组件,需要结合<mvc:annotation-driven>使用 -->    <context:component-scan base-package="com.invest.controller" />    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">    </bean>    <bean name="/hello" class="com.invest.controller.HelloWorldController"/>    <!-- 可以不加id和name -->    <bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">        <property name="mappings">            <props>                <prop key="index.do">investorController2</prop>                <prop key="3333.do">investorController3</prop>            </props>        </property>    </bean>    <bean name="investorController2" class="com.invest.controller.InvestorController2"/>    <!-- 对模型视图添加前后缀 -->    <bean id="viewResolver"        class="org.springframework.web.servlet.view.InternalResourceViewResolver"        p:prefix="/WEB-INF/view/" p:suffix=".jsp" /></beans>

然后从spring官网下载了springmvc的源码,看了一下DispatcherServlet类的启动加载过程,在debug的过程中发现一个问题。
这里写图片描述
在DispatcherServlet初始化完handlerMappings后,watch发现里面的内容为:
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@52f7b4, org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@1046021, org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@432d9d, org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@67dd20]

这里写图片描述
在spring-mvc.xml中,我明明只配置了一个BeanNameUrlHandlerMapping,但是handlerMappings里怎么会有两个?

然后我把spring-mvc.xml中配置的BeanNameUrlHandlerMapping注释掉,再去debug,handlerMappings里的内容变为:
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@560c4c, org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@1418e46, org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@e39b6f]

BeanNameUrlHandlerMapping对象只剩下了一个,那剩下的那个是哪儿来的呢?

看了一下watch里面两个对象的信息,复制到比较工具中,只有order有明显区别
这里写图片描述

继续去网上搜了一下,发现有个帖子跟我的疑问比较类似
这里写图片描述

原帖地址:http://www.cnblogs.com/beiyeren/p/3488170.html
原文如下:

最近在调试项目时,debug DispatcherServlet时,发现handlerMappings属性包含了RequestMappingHandlerMapping、SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping。可是我明明只声明了<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>真是百思不得解啊,因为是旧项目基础改造的,以为是别的地方有隐秘用法,找了半天,没找到,今天下班时,突然想到我注册了<mvc:resources location="" mapping=""/>翻看ResourcesBeanDefinitionParser,查到了关键代码:1.RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);     handlerMappingDef.setSource(source);     handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);     handlerMappingDef.getPropertyValues().add("urlMap", urlMap);2.// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"      // Register HttpRequestHandlerAdapter     MvcNamespaceUtils.registerDefaultComponents(parserContext, source);指向public static void registerDefaultComponents(ParserContext parserContext, Object source) {       registerBeanNameUrlHandlerMapping(parserContext, source);        registerHttpRequestHandlerAdapter(parserContext, source);        registerSimpleControllerHandlerAdapter(parserContext, source);    }才知道原因了。呵呵。

找到MvcNamespaceUtils的源码,debug发现是在DefaultListableBeanFactory中的beanDefinitionMap里查找bean名称为org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping的Bean定义映射关系
这里写图片描述

这里写图片描述
既然此处回去查找bean名称为org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping的Bean定义映射关系,那后面我猜想应该会根据这个映射关系去创建name为org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping的bean对象,也就是多出来的那个BeanNameUrlHandlerMapping对象,如果是这样的话,那我把自己配置的那个BeanNameUrlHandlerMapping类型的bean的name设置成org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,最后是不是就会覆盖系统自动生成的BeanNameUrlHandlerMapping对象?
经试验,不管是设置了id还是name为org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,handlermapping中最后只有一个BeanNameUrlHandlerMapping对象:

<bean name="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>

[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@3bc849, org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@da33d1, org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@17ffe93]

<bean id="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>

[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@c3dbbc, org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@8f529b, org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@178676e]

但是,如果这样设置的话,应该相当于是用我设置的对象去覆盖了系统设置的对象,还是会多了生成系统自动设置的SimpleUrlHandlerMapping对象这一步,并且在MvcNamespaceUtils类的registerBeanNameUrlHandlerMapping方法中debug,确实DefaultListableBeanFactory中的beanDefinitionMap里没有bean名称为org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping的Bean定义映射关系,应该也就证实了我的猜想。那有没有办法彻底让系统不去生成默认的SimpleUrlHandlerMapping对象呢?

我又Watch了一下beanDefinitionMap里包含的数据:
{
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0=Root bean: class [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,

org.springframework.web.servlet.handler.MappedInterceptor#0=Root bean: class [org.springframework.web.servlet.handler.MappedInterceptor]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,

org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0=Root bean: class [org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,

mvcContentNegotiationManager=Root bean: class [org.springframework.web.accept.ContentNegotiationManagerFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,

org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0=Root bean: class [org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,

org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0=Root bean: class [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0=Root bean: class [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null,

org.springframework.format.support.FormattingConversionServiceFactoryBean#0=Root bean: class [org.springframework.format.support.FormattingConversionServiceFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
}

然后在springmvc的源码中搜索了其中两个beanname
这里写图片描述
这里写图片描述
发现都是在org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser这个类中加入到context里的,看这个类应该是跟注解相关的解析类。所以我猜想是不是因为开启了注解驱动的Spring MVC组件模式的原因?

注释掉<mvc:annotation-driven />注解,再次debug,发现没有走MvcNamespaceUtils类的registerBeanNameUrlHandlerMapping方法,这样系统也就不会生成默认的BeanNameUrlHandlerMapping对象了。

其实从MvcNamespaceUtils类的registerBeanNameUrlHandlerMapping方法调用栈也可以看出来,跟AnnotationDrivenBeanDefinitionParser这个类是有关系的:
这里写图片描述

然后我又试了保留<mvc:annotation-driven />注解,注释掉自己设置的BeanNameUrlHandlerMapping,发现请求可以匹配上已经把name设置成url的controller组件。

说明系统开启了注解驱动的Spring MVC组件模式情况下生成的默认的BeanNameUrlHandlerMapping对象,用于处理Web请求与xml配置的具体请求处理控制器之间的映射匹配,是个中间人角色,也会作为Web请求和注解配置的controller组件的中间人。至于为什么会在这种情况下生成默认的BeanNameUrlHandlerMapping对象,我觉得应该是为了在使用注解配置的BeanName类型的controller时,不需要再关心HandlerMapping的问题,毕竟BeanNameUrlHandlerMapping是个底层对象,不需要再对其进行继承重写或者组合到其他对象中,那就启动的时候默认生成一个就好了。

所以如果开启了注解驱动的Spring MVC组件模式,就不需要手动在xml中配置BeanNameUrlHandlerMapping。

阅读全文
0 0
原创粉丝点击