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 十:异常处理

相关推荐
神仙别闹4 分钟前
基于Java+MySQL实现(GUI)医院管理系统
java·mysql·oracle
吴声子夜歌35 分钟前
Java——显示条件
java·开发语言
AC赳赳老秦40 分钟前
OpenClaw与WPS宏联动:批量执行WPS复杂操作,解决办公表格批量处理难题
java·前端·数据库·自动化·需求分析·deepseek·openclaw
ze^01 小时前
Day01 Web应用&架构搭建&域名源码&站库分离&MVC模型&解析受限&对应路径
安全·web安全·架构·mvc·安全架构
bupt_011 小时前
Hermes深入理解及源码解析(二):Hermes的记忆机制
java·服务器·前端
青云计划1 小时前
SpringSecurity技术文档
spring
Ting-yu1 小时前
Spring AI Alibaba零基础速成(1) ---- 项目创建与配置
java·人工智能·spring
喜欢coding的谢同学1 小时前
ArthasClaw:用自然语言诊断 JVM 的 AI 助手,告别繁琐的 Arthas 命令
java·人工智能·arthas
架构源启2 小时前
Spring AI完整学习路线:从Java开发到AI Agent的进阶之路(附15篇实战教程)
java·人工智能·spring
SPC的存折2 小时前
20、K8S-Pod驱逐
java·docker·kubernetes