标签默认会开启SpringMVC的注解驱动模式,默认注册一个RequestMappingHandlerMapping、一个RequestMappingHandlerAdapter、一个ExceptionHandlerExceptionResolver。以支持对使用了 @RequestMapping 、 @ExceptionHandler 及其他注解的控制器方法的请求处理。
接下来结合几个小例子,来分析一下
标签的解析过程,也为下一步分析SpringMVC的处理流程做好准备。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!--ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- DispatcherServlet -->
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>/example/*
contextConfigLocation
classpath:spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 扫描Controller包 -->
<context:component-scan base-package="com.lyc.cn">
<!--只扫描包下Controller注解的类-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--==========mvc:annotation-driven 相关 begin==========-->
<!--
开启注解模式驱动:
默认注册一个RequestMappingHandlerMapping,一个RequestMappingHandlerAdapter,一个ExceptionHandlerExceptionResolver。
以支持对使用了 @RequestMapping 、 @ExceptionHandler 及其他注解的控制器方法的请求处理。
-->
<!--conversion-service:数据转换、格式化-->
<!--validator:数据校验-->
<!--content-negotiation-manager:多视图内容协商-->
<!--enable-matrix-variables:是否开启矩阵变量-->
<!--ignore-default-model-on-redirect:在重定向时是否忽略默认model的内容,默认为true-->
<!--conversion-service="conversionService"-->
<mvc:annotation-driven
validator="validator"
content-negotiation-manager="contentNegotiationManager"
enable-matrix-variables="true"
ignore-default-model-on-redirect="true"
message-codes-resolver="messageCodeResolver">
<!--argument-resolvers:参数解析处理-->
<mvc:argument-resolvers>
<ref bean="myHandlerMethodArgumentResolver"/>
</mvc:argument-resolvers>
<!--return-value-handlers:返回参数解析处理-->
<mvc:return-value-handlers>
<ref bean="myHandlerMethodReturnValueHandler"/>
</mvc:return-value-handlers>
<!--path-matching:路径匹配,参考PathMatchConfigurer-->
<mvc:path-matching suffix-pattern="true" trailing-slash="false" registered-suffixes-only="true"/>
<!--message-converters:消息转换-->
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="objectMapper"/>
</bean>
<bean class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
<property name="objectMapper" ref="xmlMapper"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--conversionService-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!--Spring的Converter可以将一种类型转换成另一种类型。-->
<!--在使用时,必须编写一个实现org.springframework.core.convert.converter.Converter接口的java类。-->
<property name="converters">
<set>
<bean class="com.lyc.cn.my.conversion.MyConverter">
<property name="datePattern" value="yyyy-MM-dd"/>
</bean>
</set>
</property>
<!--Formatter和Converter一样,也是将一种类型转换成另一种类型。但是,Formatter的源类型必须是一个String。-->
<!--在使用时,必须编写一个实现org.springframework.format.Formatter接口的java类。-->
<property name="formatters">
<set>
<bean class="com.lyc.cn.my.conversion.MyFormatter">
<property name="datePattern" value="yyyy-MM-dd"/>
</bean>
</set>
</property>
<!--注册Formatter的另一种方法是使用Registrar。-->
<!--先需要实现org.springframework.format.FormatterRegistrar接口,-->
<property name="formatterRegistrars">
<set>
<bean class="com.lyc.cn.my.conversion.MyFormatterRegistrar">
<constructor-arg name="datePattern" value="yyyy-MM-dd"/>
</bean>
</set>
</property>
<!--
Converter是一般工具,可以将一种类型转换成另一种类型。
例如,将String转换成Date,或者将Long转换成Date。Converter既可以用在web层,也可以用在其它层中。
Formatter只能将String转成成另一种java类型。
例如,将String转换成Date,但它不能将Long转换成Date。所以,Formatter适用于web层。
为此,在Spring MVC应用程序中,选择Formatter比选择Converter更合适。
-->
</bean>
<!--validator-->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<property name="validationMessageSource" ref="validationMessageResource"/>
</bean>
<bean id="validationMessageResource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:validationMessageSource</value>
</list>
</property>
</bean>
<!--contentNegotiationManager-->
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
<!--messageCodeResolver-->
<bean id="messageCodeResolver" class="org.springframework.validation.DefaultMessageCodesResolver">
<property name="prefix" value="validation."></property>
</bean>
<!--Adapter-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="myHandlerMethodArgumentResolver" class="com.lyc.cn.my.argumentResolvers.MyHandlerMethodArgumentResolver"/>
<bean id="myHandlerMethodReturnValueHandler" class="com.lyc.cn.my.returnValueHandlers.MyHandlerMethodReturnValueHandler"/>
<!--message-converters:消息转换-->
<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="indentOutput" value="true"/>
<property name="simpleDateFormat" value="yyyy-MM-dd"/>
<property name="modulesToInstall" value="com.fasterxml.jackson.module.paramnames.ParameterNamesModule"></property>
</bean>
<bean id="xmlMapper" parent="objectMapper" p:createXmlMapper="true"/>
<!--==========mvc:annotation-driven 相关 End==========-->
<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 制定页面存放的路径 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 文件的后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- MultipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设定默认编码 -->
<property name="defaultEncoding" value="UTF-8"/>
<!-- 设定文件上传的最大值为5MB,5*1024*1024 -->
<property name="maxUploadSize" value="5242880"/>
<!-- 设定文件上传时写入内存的最大值,如果小于这个参数不会生成临时文件,默认为10240 -->
<property name="maxInMemorySize" value="40960"/>
<!-- 上传文件的临时路径 -->
<property name="uploadTempDir" value="WEB-INF/fileUpload"></property>
<!-- 延迟文件解析 -->
<property name="resolveLazily" value="true"/>
</bean>
<!--拦截器-->
<mvc:interceptors>
<bean id="myHandlerInterceptor" class="com.lyc.cn.my.interceptor.MyHandlerInterceptor"></bean>
</mvc:interceptors>
</beans>
标签的解析,留在后面分析SpringMVC流程的时候再粘贴吧。关于定位自定义标签解析的过程,前面已经多有介绍,这里直接打开AnnotationDrivenBeanDefinitionParser类并定位到其parse方法。
/**
* 解析 mvc:annotation-driven 标签
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
XmlReaderContext readerContext = parserContext.getReaderContext();
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
parserContext.pushContainingComponent(compDefinition);
/**
* 获取协商内容视图配置
*/
RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);
/**
* 创建RequestMappingHandlerMapping的RootBeanDefinition
* 从这里也可以看出,开启mvc:annotation-driven标签后,
* 将会默认注册RequestMappingHandlerMapping作为默认的HandlerMapping
*/
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("order", 0);
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
// 是否开启矩阵变量
if (element.hasAttribute("enable-matrix-variables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
// 解析path-matching路径匹配标签
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
// 解析cors跨域标签
RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);
// 解析conversion-service数据转换、格式化标签
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
// 解析validator标签
RuntimeBeanReference validator = getValidator(element, source, parserContext);
// 解析message-codes-resolver标签
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
/**
* 创建ConfigurableWebBindingInitializer的RootBeanDefinition对象
* 并将上一步解析的conversionService、validator、messageCodesResolver
* 作为属性注入到该对象中
*/
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
bindingDef.setSource(source);
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
bindingDef.getPropertyValues().add("conversionService", conversionService);
bindingDef.getPropertyValues().add("validator", validator);
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
// 解析message-converters标签
ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
// 解析argument-resolvers标签
ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);
// 解析return-value-handlers标签
ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);
// 解析async-support标签
String asyncTimeout = getAsyncTimeout(element);
// 解析async-support的task-executor子标签
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
// 解析async-support的callable-interceptors子标签
ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
// 解析async-support的deferred-result-interceptors子标签
ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);
/**
* 创建RequestMappingHandlerAdapter的RootBeanDefinition
* 从这里也可以看出,开启mvc:annotation-driven标签后,
* 将会默认注册RequestMappingHandlerAdapter作为默认的HandlerAdapter
* 并将上面解析的内容绑定到该HandlerAdapter中
*/
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
addRequestBodyAdvice(handlerAdapterDef);
addResponseBodyAdvice(handlerAdapterDef);
if (element.hasAttribute("ignore-default-model-on-redirect")) {
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
if (argumentResolvers != null) {
handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
if (asyncTimeout != null) {
handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
}
if (asyncExecutor != null) {
handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
}
handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME , handlerAdapterDef);
/**
* 创建CompositeUriComponentsContributorFactoryBean的RootBeanDefinition
* CompositeUriComponentsContributorFactoryBean是一个工厂bean,
* 可以用来获取RequestMappingHandlerAdapter中的HandlerMethodArgumentResolver配置
*/
RootBeanDefinition uriContributorDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
uriContributorDef.setSource(source);
uriContributorDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
uriContributorDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
String uriContributorName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
readerContext.getRegistry().registerBeanDefinition(uriContributorName, uriContributorDef);
/**
* 创建ConversionServiceExposingInterceptor的RootBeanDefinition
* 主要用来解析spring:eval标签
*/
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source);
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedInterceptorDef.setSource(source);
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);
/**
* 创建ExceptionHandlerExceptionResolver的RootBeanDefinition
*/
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
methodExceptionResolver.setSource(source);
methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
methodExceptionResolver.getPropertyValues().add("order", 0);
addResponseBodyAdvice(methodExceptionResolver);
if (argumentResolvers != null) {
methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);
/**
* 创建ResponseStatusExceptionResolver的RootBeanDefinition
*
*/
RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
statusExceptionResolver.setSource(source);
statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
statusExceptionResolver.getPropertyValues().add("order", 1);
String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);
/**
* 创建DefaultHandlerExceptionResolver的RootBeanDefinition
* 该类是HandlerExceptionResolver的默认实现,可以解析http异常并将相应的http状态码返回
* 例如:404
*/
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
defaultExceptionResolver.getPropertyValues().add("order", 2);
String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);
/**
* 将上面创建的RootBeanDefinition以组件形式纳入SpringIOC容器
*/
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));
parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
parserContext.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));
parserContext.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
// 注册默认组件
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
parserContext.popAndRegisterContainingComponent();
return null;
}
该方法有点长,但是其中的逻辑都是很简单的。那么我们挑选getContentNegotiationManager作为特例来看一下其具体的解析思路:
private RuntimeBeanReference getContentNegotiationManager(
Element element, @Nullable Object source, ParserContext parserContext) {
RuntimeBeanReference beanRef;
if (element.hasAttribute("content-negotiation-manager")) {
String name = element.getAttribute("content-negotiation-manager");
beanRef = new RuntimeBeanReference(name);
}
else {
RootBeanDefinition factoryBeanDef = new RootBeanDefinition(ContentNegotiationManagerFactoryBean.class);
factoryBeanDef.setSource(source);
factoryBeanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
factoryBeanDef.getPropertyValues().add("mediaTypes", getDefaultMediaTypes());
String name = CONTENT_NEGOTIATION_MANAGER_BEAN_NAME;
parserContext.getReaderContext().getRegistry().registerBeanDefinition(name , factoryBeanDef);
parserContext.registerComponent(new BeanComponentDefinition(factoryBeanDef, name));
beanRef = new RuntimeBeanReference(name);
}
return beanRef;
}
非常之简单,如果我们自定义了content-negotiation-manager标签,那么则使用自定义的视图协商;否则创建一个默认的视图协商。仅此而已,而且关于其他的标签解析也是同样的套路。感兴趣的同学可以自己查看。
OK,那么接下来我们需要分析一下,如果
没有配置任何子标签的话,Spring会如何处理呢?
可以看到即使不做任何子标签的配置,SpringMVC默认也会创建上述9个内部bean的实例。 关键的是RequestMappingHandlerMapping、RequestMappingHandlerAdapter两个,关于这两个类的作用及其使用过程,会在下面继续分析。