Spring MVC 初始化源码—<mvc:annotation-driven >配置标签的源码解析
Posted L-Java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring MVC 初始化源码—<mvc:annotation-driven >配置标签的源码解析相关的知识,希望对你有一定的参考价值。
基于最新Spring 5.x,详细介绍了Spring MVC 初始化流程的源码,主要包括<mvc:annotation-driven >配置标签的源码解析。
我正在参与CSDN《新程序员》有奖征文,活动地址:https://marketing.csdn.net/p/52c37904f6e1b69dc392234fff425442。
此前我们讲过了DispatcherServlet与子容器的初始化以及MVC组件的初始化,我们说过如果IoC容器中存在给定类型的组件,那么DispatcherServlet#onRefresh()
方法中就不会尝试初始化默认组件了。
而在初始化IoC子容器的时候,将会解析Spring MVC的配置文件,在配置文件中就有可能初始化我们自定义的一些组件bean,也有可能会加载其他MVC配置标签,最常见的就是<mvc:annotation-driven >
标签,实际上该标签就会向容器注册一些组件bean,并且还会进行额外的配置。
<mvc:annotation-driven>
标签为基于注解的SpringMVC驱动提供便捷配置,虽然Spring mvc5版本之后Spring MVC会默认加载大部分的默认组件,但是使用该标签仍然能够自动加载某些额外配置,比如自动发现并注册MappingJackson2HttpMessageConverter
转换器以支持JSON数据交互的请求和响应。
mvc系列标签同样属于扩展标签,根据我们此前学过的IoC容器初始化源码,扩展标签将会在IoC容器初始化的parseCustomElement
方法中解析,mvc标签的解析器位于MvcNamespaceHandler
类中:
可以知道<mvc:annotation-driven >
标签是由AnnotationDrivenBeanDefinitionParser
这个解析器来解析的。
下面的源码版本基于5.2.8.RELEASE
。源码解析基于传统的SSM项目,不适用于Spring Boot项目。
Spring MVC源码 系列文章
Spring MVC 初始化源码(1)—ContextLoaderListener与父上下文容器的初始化
Spring MVC 初始化源码(2)—DispatcherServlet与子容器的初始化以及MVC组件的初始化【一万字】
Spring MVC 初始化源码(3)—<mvc:annotation-driven >配置标签的源码解析
Spring MVC 初始化源码(4)—@RequestMapping注解的源码解析
文章目录
- Spring MVC源码 系列文章
- 1 AnnotationDrivenBeanDefinitionParser静态块
- 2 parse执行解析
- 3 总结
1 AnnotationDrivenBeanDefinitionParser静态块
首先我们能看到AnnotationDrivenBeanDefinitionParser具有静态代码块,其内部的代码主要是查找是否具有某些依赖,用于后续parse方法中的一些自动配置,比如自动注册MappingJackson2HttpMessageConverter,用于支持application/json请求和响应。
//AnnotationDrivenBeanDefinitionParser的属性
private static final boolean javaxValidationPresent;
private static boolean romePresent;
private static final boolean jaxb2Present;
private static final boolean jackson2Present;
private static final boolean jackson2XmlPresent;
private static final boolean jackson2SmilePresent;
private static final boolean jackson2CborPresent;
private static final boolean gsonPresent;
static {
ClassLoader classLoader = AnnotationDrivenBeanDefinitionParser.class.getClassLoader();
//项目是否具有javax.validation.Validator接口,即validation-api的依赖
javaxValidationPresent = ClassUtils.isPresent("javax.validation.Validator", classLoader);
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
//项目是否具有javax.xml.bind.Binder接口,这位于rt.jar核心包中
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
//项目是否具有jackson-databind依赖,主要用于支持application/json请求和响应,实现JSON的序列化和反序列化
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
//项目是否具有jackson-dataformat-xml依赖,主要用于支持XML的请求和响应,实现XML的序列化和反序列化
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
//项目是否具有jackson-dataformat-smile依赖
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
//项目是否具有jackson-dataformat-cbor依赖
jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
//项目是否具有gson依赖
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
}
2 parse执行解析
AnnotationDrivenBeanDefinitionParser
的方法就是解析<mvc:annotation-driven/>
标签的核心方法。
在该方法中,将会创建并配置多个mvc组件的bean定义到Spring容器中,这样的话,在后续的DispatcherServlet#onRefresh()
方法中就不会初始化对应类型的默认组件了。
从源码中可以看到,则将会注册RequestMappingHandlerMapping、RequestMappingHandlerAdapter、CompositeUriComponentsContributorFactoryBean、MappedInterceptor、ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver、 BeanNameUrlHandlerMapping、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、HandlerMappingIntrospector
这11
个组件的bean定义到容器中。
如果只存在该标签而没有其他子标签配置,则还会注册ContentNegotiationManagerFactoryBean、FormattingConversionServiceFactoryBean、OptionalValidatorFactoryBean、LinkedHashMap(用于CORS配置)
这4
个bean定义到容器中。
该方法的源码很多,我们拆分开来讲解。最重要最常见的就是RequestMappingHandlerMapping和RequestMappingHandlerAdapter
这两个bean定义了,因为它们用于支持基于注解的Spring MVC处理。
/**
* 自动配置的RequestMappingHandlerMapping的beanName
*/
public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();
/**
* 自动配置的RequestMappingHandlerAdapter的beanName
*/
public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();
public static final String CONTENT_NEGOTIATION_MANAGER_BEAN_NAME = "mvcContentNegotiationManager";
/**
* AnnotationDrivenBeanDefinitionParser的方法
* 解析<mvc:annotation-driven/>标签
*
* @param element 当前<mvc:annotation-driven/>标签元素
* @param context 解析上下文,从中可以获取各种配置
* @return
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
Object source = context.extractSource(element);
XmlReaderContext readerContext = context.getReaderContext();
/*
* 为<mvc:annotation-driven/>标签创建一个CompositeComponentDefinition类型的bean定义组件
* 该类型的bean定义,内部可以包含多个bean定义,因此又称为复合bean定义
*/
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
//存入解析上下文内部的containingComponents集合中,入栈顶
context.pushContainingComponent(compDefinition);
/*内容协商处理,默认会注册ContentNegotiationManagerFactoryBean的bean定义*/
RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, context);
/*
* 1 向容器注册RequestMappingHandlerMapping的bean定义
* 该类可以处理基于注解的映射
*/
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//添加order属性,使其位于handlerMappings集合链首位
handlerMappingDef.getPropertyValues().add("order", 0);
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
/*
* 路径矩阵变量的配置
*/
//如果具有enable-matrix-variables属性,即是否支持矩阵变量的属性
if (element.hasAttribute("enable-matrix-variables")) {
//获取该属性的值
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
//添加removeSemicolonContent属性
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
/*
* 一系列路径匹配参数的配置
*/
configurePathMatchingProperties(handlerMappingDef, element, context);
//当前的RequestMappingHandlerMapping的bean定义注册到子容器中
//注册此RequestMappingHandlerMapping的bean定义到容器中,名为"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);
/*
* cors跨域解决的配置
* 默认会注册LinkedHashMap的bean定义
*/
RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);
/*
* 2 创建ConfigurableWebBindingInitializer的bean定义
* 该类用于在Spring应用程序上下文中基于声明式的配置WebDataBinder,允许与多个控制器/处理程序一起重用预配置的初始化程序。
*/
/*
* 解析"conversion-service"属性获取转换服务,
* 默认会注册FormattingConversionServiceFactoryBean的bean定义,这是一个FactoryBean,实际是DefaultFormattingConversionService类型
*/
RuntimeBeanReference conversionService = getConversionService(element, source, context);
/*
* 解析"validator"属性获取校验器
* 默认会注册OptionalValidatorFactoryBean的bean定义,内部使用一种provider实现,通常是hibernate-validator依赖中的HibernateValidator
*/
RuntimeBeanReference validator = getValidator(element, source, context);
/*解析"message-codes-resolver"属性获取响应码解析器,默认是null*/
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
/*新建ConfigurableWebBindingInitializer的bean定义*/
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
bindingDef.setSource(source);
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//设置conversionService属性
bindingDef.getPropertyValues().add("conversionService", conversionService);
//设置validator属性
bindingDef.getPropertyValues().add("validator", validator);
//设置messageCodesResolver属性
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
/*
* 解析<mvc:message-converters/>子标签,获取并配置消息转换器HttpMessageConverter
* 主要用于处理基于ContentType的请求参数转换、封装
*
* 默认情况下会注册一系列的HttpMessageConverter
* 在这其中,就会尝试自动配置配置MappingJackson2HttpMessageConverter转换器(前提是项目具有jackson-databind依赖)
*/
ManagedList<?> messageConverters = getMessageConverters(element, source, context);
/*
* 解析<mvc:argument-resolvers/>子标签,获取并配置方法参数解析器HandlerMethodArgumentResolver
* 用于将给定请求的上下文中的数据解析为HandlerMethod方法的参数,相比于HttpMessageConverter,它更加上层
* 即它负责处理Handler方法里的所有入参:包括自动封装、自动赋值、校验等等,
* 具体的实现交给不同的子类来做,比如基于Name、基于ContentType
*
* 默认情况下为null
*/
ManagedList<?> argumentResolvers = getArgumentResolvers(element, context);
/*
* 解析<mvc:return-value-handlers/>子标签,获取并配置返回值处理器HandlerMethodReturnValueHandler
* 返回值处理器用于处理从处理程序方法调用返回的值
*
* 默认情况下为null
*/
ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, context);
/*
* 解析<mvc:async-support/>子标签,获取异步处理的超时时间
* 就是解析此<mvc:annotation-driven/>标签内部的<mvc:async-support/>子标签的default-timeout属性
*
* 默认情况下为null
*/
String asyncTimeout = getAsyncTimeout(element);
/*
* 解析<mvc:async-support/>子标签,获取异步处理的线程池
* 就是解析此<mvc:annotation-driven/>标签内部的<mvc:async-support/>子标签的task-executor属性
*
* 默认情况下为null
*/
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
/*
* 解析<mvc:async-support/>子标签,获取异步处理CallableProcessingInterceptor类型拦截器
* 就是解析此<mvc:annotation-driven/>标签内部的<mvc:async-support/>子标签的<mvc:callable-interceptors/>子标签
*
* 默认情况下为空集合
*/
ManagedList<?> callableInterceptors = getInterceptors(element, source, context, "callable-interceptors");
/*
* 解析<mvc:async-support/>子标签,获取异步处理DeferredResultProcessingInterceptor类型拦截器
* 就是解析此<mvc:annotation-driven/>标签内部的<mvc:async-support/>子标签的<mvc:deferred-result-interceptors>子标签
*
* 默认情况下为空集合
*/
ManagedList<?> deferredResultInterceptors = getInterceptors(element, source, context, "deferred-result-interceptors");
/*
* 3 向容器注册RequestMappingHandlerAdapter的bean定义
* 该类可以帮助调用找到的Handler
*/
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//内容协商处理
handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
//WebDataBinder的初始化器
handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
//消息转换器
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
/*
* 如果具有jackson-databind依赖
* 那么新建一个JsonViewRequestBodyAdvice、JsonViewResponseBodyAdvice类型的bean定义
* 作为RequestMappingHandlerAdapter的requestBodyAdvice、responseBodyAdvice属性
*
* 用于支持具有Spring MVC的 @RequestMapping 或者 @ExceptionHandler的 方法上的@JsonView注解的解析
*/
addRequestBodyAdvice(handlerAdapterDef);
addResponseBodyAdvice(handlerAdapterDef);
/*
* 解析"ignore-default-model-on-redirect"属性,设置给RequestMappingHandlerAdapter的ignoreDefaultModelOnRedirect属性
* 该属性用于确定在重定向中是否永远不会使用默认model属性,即使控制器方法中未声明RedirectAttributes参数也是如此。
* 将其设置为 false 意味着如果控制器方法不声明RedirectAttributes参数,则默认model可能在重定向中使用,默认值为false。
*/
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);
//注册此RequestMappingHandlerAdapter的bean定义到容器中,名为"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"
readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
/*
* 4 向容器注册CompositeUriComponentsContributorFactoryBean的bean定义
*
* 该类是一个FactoryBean,可以获取CompositeUriComponentsContributor
* 用于辅助MvcUriComponentsBuilder构建UriComponentsBuilder
*/
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);
/*
* 5 新建一个ConversionServiceExposingInterceptor的bean定义
*
* 这是一个Spring MVC的拦截器HandlerInterceptor的实现
* 该拦截器用于在preHandle方法中将已配置的ConversionService放置在请求request的属性中(request.setAttribute)
* 以便在请求处理期间可用。请求属性名称是"org.springframework.core.convert.ConversionService"
*/
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source);
//加入转换服务
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
/*
* 5 向容器注册一个MappedInterceptor的bean定义
*
* 这是一个Spring MVC的拦截器HandlerInterceptor的实现
* 该拦截器内部还包含一个拦截器,并利用内部的拦截器真正的执行功能
* MappedInterceptor它仅仅提供了匹配的功能以测试拦截器是否适用于给定的请求路径。
*/
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedInterceptorDef.setSource(source);
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//设置构造器参数
//第一个参数includePatterns为null
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
//第二个参数HandlerInterceptor为上面定义的ConversionServiceExposingInterceptor
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
/*将MappedInterceptor注册到IoC容器中*/
String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);
/*
* 6 向容器注册一个ExceptionHandlerExceptionResolver的bean定义
*
* 这是一个Spring MVC的异常处理器HandlerExceptionResolver的实现
* 该异常处理器可以解析基于@ExceptionHandler注解的异常处理方法
*/
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
methodExceptionResolver.setSource(source);
methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//内容协商处理
methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
//消息转换器
methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
//期望排在handlerExceptionResolvers异常处理器集合链的首位
methodExceptionResolver.getPropertyValues().add("order", 0);
/*
* 如果具有jackson-databind依赖
* 那么新建一个JsonViewResponseBodyAdvice类型的bean定义
* 作为RequestMappingHandlerAdapter的responseBodyAdvice属性
*
* 用于支持具有Spring MVC的 @RequestMapping 或者 @ExceptionHandler的 方法上的@JsonView注解的解析
*
* 这些通知应用于在使用@ResponseBody注释的控制器方法执行或返回ReturnEntity之后,
* 但在将主体使用选定的HttpMessageConverter写入响应之前,添加一个或多个要调用的组件。
*/
addResponseBodyAdvice(methodExceptionResolver);
if (argumentResolvers != null) {
methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
//返回值处理器
if (returnValueHandlers != null) {
methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
/*将ExceptionHandlerExceptionResolver注册到IoC容器中*/
String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);
/*
* 7 向容器注册一个ResponseStatusExceptionResolver的bean定义
*
* 这是一个Spring MVC的异常处理器HandlerExceptionResolver的实现
* 该异常处理器可以解析使用@ResponseStatus注解表示的异常,将注解中的值映射到HTTP状态代码并返回给客户端。
*/
RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
statusExceptionResolver.setSource(source);
statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//期望排在handlerExceptionResolvers异常处理器集合链的第二位
statusExceptionResolver.getPropertyValues().add("order", 1);
/*将ResponseStatusExceptionResolver注册到IoC容器中*/
String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);
/*
* 7 向容器注册一个DefaultHandlerExceptionResolver的bean定义
*
* 这是一个Spring MVC的异常处理器HandlerExceptionResolver的实现
* 该异常处理器可以解析Spring MVC 引发的异常,将它们映射到HTTP状态代码并返回给客户端。
*/
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//期望排在handlerExceptionResolvers异常处理器集合链的第三位
defaultExceptionResolver.getPropertyValues().add("order", 2);
/*将ResponseStatusExceptionResolver注册到IoC容器中*/
String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);
context.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME)Spring MVC 初始化源码—@RequestMapping注解的源码解析
Spring MVC源码——Root WebApplicationContext