Spring MVC 五:DispatcherServlet初始化之 mvc:annotation-driven

通过xml方式初始化DispatcherServlet时,xml文件中可以配置:

复制代码
<mvc:annotation-driven />

或:

复制代码
   <mvc:annotation-driven >
        <!--设置响应输出字符集-->
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=utf-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

表示对MVC注解的支持。

该标签的解析工作最终会由AnnotationDrivenBeanDefinitionParser的parse方法完成,parse方法主要完成以下工作

  1. 注册RequestMappingHandlerMapping的beanDefinition到WebApplication容器
  2. 注册RequestMappingHandlerAdapter的beanDefinition到WebApplication容器,同时从xml文件解析其messageConverters的定义(这也是xml文件中messageConverters生效的原因)
  3. 注册ExceptionHandlerExceptionResolver的beanDefinition到WebApplication容器
  4. 注册ResponseStatusExceptionResolver的beanDefinition到WebApplication容器
  5. 注册DefaultHandlerExceptionResolver的beanDefinition到WebApplication容器

之后在容器refresh的过程中根据beanDefinition创建对应的bean。

之后,在DispatcherServlet初始化的过程中,通过initStrategies初始化SpringMVC的相关"特殊类型的Bean":

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

比如initHandlerMappings方法:

复制代码
private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            //首先通过beanFactoryUtils从Spring容器中,通过类型获取
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            //如果从容器中获取到了bean,放入到handlerMappings中
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(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) {
            //从容器中没有获取到,才调用getDefaultStrategies获取默认的、dispatcherServlet.properties文件中定义的
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

如果xml文件中不配置<mvc:annotation-driven />,也没有 <mvc:default-servlet-handler />,则Spring容器中就不会有对应的bean,这种情况下,才通过调用getDefaultStrategies方法,读取DispatcherServlet.properties文件获取该文件定义的默认HandlerMappings,完成创建,当然,创建后也会放入Spring容器:

复制代码
	protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
		return context.getAutowireCapableBeanFactory().createBean(clazz);
	}
DefaultServletHandlerBeanDefinitionParser

xml文件中如果配置了 <mvc:default-servlet-handler />,SpringMVC启动时解析 <mvc:default-servlet-handler />标签过程中会调用DefaultServletHandlerBeanDefinitionParser,其parse方法会将SimpleUrlHandlerMapping以及BeanNameUrlHandlerMapping注入到WebApplicationContext容器中,目的是通过SimpleUrlHandlerMapping来处理"/**"请求(转交给DefaultServletHttpRequestHandler)。

这个处理有个副作用:由于SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping作为handlerMappings注入到容器中,分析DispatcherServlet的初始化方法initHandlerMappings的源码可知:DispatcherServlet.properties文件中默认的RequestMappingHandlerMapping不会被注入!而RequestMappingHandlerMapping是具体负责SpringMVC注解解释的,没有了RequestMappingHandlerMapping,类似@Controller等SpringMVC注解就不会生效。

所以,其实SpringMVC默认情况下天生是可以支持SpringMVC注解的,只不过是 <mvc:default-servlet-handler />往往不可避免的需要配置(因为项目中总有静态资源需要展示),但是加了 <mvc:default-servlet-handler />会导致SpringMVC的注解失效!

因此导致了,正常的SpringMVC项目的以下两项配置往往需要成对出现:

复制代码
 <mvc:annotation-driven />
 <mvc:default-servlet-handler />

上一篇 Spring MVC 十:异常处理

相关推荐
a程序小傲9 分钟前
高并发下如何防止重复下单?
java·开发语言·算法·面试·职场和发展·状态模式
sww_102627 分钟前
智能问数系统(二):数据分析师Python
java·前端·python
2301_7813925234 分钟前
MySQL格式化数据展示——分页查询
java·数据库·mysql·性能优化
Java后端的Ai之路1 小时前
【Java教程】- 并发编程核心知识解读
java·开发语言·并发编程
椰羊~王小美1 小时前
为什么@Builder 注解默认父类字段不可见
java
一 乐1 小时前
学生宿舍管理|基于springboot + vue学生宿舍管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·助农电商系统
一人の梅雨1 小时前
义乌购商品详情接口进阶实战:批发场景下的精准解析与高可用架构
java·服务器·前端
Dontla1 小时前
Mybatis Introduction (Java ORM Framework)
java·开发语言·mybatis
信码由缰1 小时前
JExten:基于Java模块系统(JPMS)构建健壮的插件架构
java·开发语言·架构
NuageL1 小时前
SpringBoot使用@Scheduled注解实现定时任务
java·spring boot·后端