您的位置:首页 > 移动开发

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

2017-09-04 15:44 465 查看
最近在研究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/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring mvc 注解 handler