<mvc:annotation-driven/>
,这个标签会帮我们注入很多关键而实用的bean,但是用它也得小心跟自己手动注入的bean重复,会造成不必要的麻烦。所以今天来了解下这个标签。本篇笔记主要分析SpringMVC 5.1.1 这个版本。
BeanDefinitionParser
接口的实现类来完成的。我们今天研究的是<mvc:annotation-driven/>
标签,所以我们找到对应的实现类是org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser
。1 简单了解下功能
AnnotationDrivenBeanDefinitionParser
,为 <annotation-driven />
MVC名称空间元素提供配置。
1.1 注册以下HandlerMappings (映射器们):
RequestMappingHandlerMapping
的排序为0,用于将请求映射到带@RequestMapping注释的控制器方法。BeanNameUrlHandlerMapping
在排序为2,以将URL路径映射到控制器bean名称。
1.2 注册以下HandlerAdapters (适配器们):
RequestMappingHandlerAdapter
用于使用带@RequestMapping注解的控制器方法处理请求。HttpRequestHandlerAdapter
用于使用HttpRequestHandlers处理请求。SimpleControllerHandlerAdapter
用于使用基于接口的控制器处理请求。
1.3 注册以下HandlerExceptionResolvers (异常处理解析器们):
ExceptionHandlerExceptionResolver
,用于通过org.springframework.web.bind.annotation.ExceptionHandler
方法处理异常。ResponseStatusExceptionResolver
用于使用org.springframework.web.bind.annotation.ResponseStatus
注释的异常。DefaultHandlerExceptionResolver
用于解析已知的Spring异常类型
1.4 其他
注册 org.springframework.util.AntPathMatcher
和 org.springframework.web.util.UrlPathHelper
以供 RequestMappingHandlerMapping
、ViewControllers
的 HandlerMapping
和 HandlerMapping
服务资源是使用。
对于JSR-303实现,会检测 javax.validation.Validator
路径是否有效,有效则会帮我们创建对应的实现类并注入。
最后帮我们检测一些列 HttpMessageConverter
的实现类们,这些主要是用作直接对请求体里面解析出来的数据进行转换。俗称 http 消息转换器,与参数转换器不一样。
在 SpringMVC 5.1.1 中有以下几个检测:
检测路径 | 注入消息转换器 | 对应请求类型 |
---|---|---|
com.rometools.rome.feed.WireFeed | RssChannelHttpMessageConverter | application/atom+xml |
javax.xml.bind.Binder | Jaxb2RootElementHttpMessageConverter | application/xml |
com.fasterxml.jackson.databind.ObjectMapper & com.fasterxml.jackson.core.JsonGenerator | MappingJackson2HttpMessageConverter | application/json |
com.fasterxml.jackson.dataformat.xml.XmlMapper | MappingJackson2XmlHttpMessageConverter | application/xml |
com.fasterxml.jackson.dataformat.smile.SmileFactory | MappingJackson2SmileHttpMessageConverter | application/x-jackson-smile |
com.fasterxml.jackson.dataformat.cbor.CBORFactory | MappingJackson2CborHttpMessageConverter | application/cbor |
com.google.gson.Gson | GsonHttpMessageConverter | application/json |
除了会帮我们注入以上检测有效的 http 消息转换器外,还会帮我们注入SpringMVC自带的几个 http 消息转换器,上面检测的转换器是由上到下顺序加入的,也就是说解析的时候回根据 ContentType 从上到下找合适的。
2 源码简介
该标签的解释是在 org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser
类的 parse(..)
方法中
2.1 HandlerMappings 注册
2.1.1 RequestMappingHandlerMapping
映射器的注册
//生成RequestMappingHandlerMapping组件对象 RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); handlerMappingDef.setSource(source); handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //优先级设置为最高 handlerMappingDef.getPropertyValues().add("order", 0); //添加contentNegotiationManager属性,处理media type 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); } //配置路径匹配解析器等属性 configurePathMatchingProperties(handlerMappingDef, element, context); readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef); //将RequestMappingHandlerMapping注册为bean对象放置bean工厂中 RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source); handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);
2.1.1 BeanNameUrlHandlerMapping
映射器注册就比较随意
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off" MvcNamespaceUtils.registerDefaultComponents(context, source);
放在这里跟 HttpRequestHandlerAdapter
、SimpleControllerHandlerAdapter
、HandlerMappingIntrospector
一起注册的。
2.2 HandlerAdapters 注册
2.2.1 RequestMappingHandlerAdapter
适配器的注册
//从该标签的 "conversion-service" 属性中获取注入到容器里面的参数转换服务器, 没有则重新创建 RuntimeBeanReference conversionService = getConversionService(element, source, context); //从该标签的 "validator" 属性中获取注入到容器里面的参数验证服务, 没有则创建 RuntimeBeanReference validator = getValidator(element, source, context); //从该标签的 "message-codes-resolver" 属性中获取错误码解析器, 没有则不创建 RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element); //创建 `WebDataBinder` 初始化使用到的记录器, 并将上面的参数转换和验证相关绑定在其上面 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, context); //从该标签的 `argument-resolvers` 属性上获取记录自定义参数转换器的集合 ManagedList<?> argumentResolvers = getArgumentResolvers(element, context); //从该标签的 `return-value-handlers` 属性上获取记录自定义返回值转换器的集合 ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, context); //从该标签的 "async-support" 子节点解析,获取其中的 "default-timeout" 属性,作为异步处理超时时间,默认null String asyncTimeout = getAsyncTimeout(element); //从该标签的 "async-support" 子节点解析,获取其中的 "task-executor" 属性,异步任务线程池 RuntimeBeanReference asyncExecutor = getAsyncExecutor(element); //从该标签的 "async-support" 子节点解析,获取其中的 "callable-interceptors"节点,异步处理callable类型拦截器 ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, context); ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, context); //生成RequestMappingHandlerAdapter组件对象,并将上面获取的相关绑定在该映射器上 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);
2.2.2 HttpRequestHandlerAdapter
和 SimpleControllerHandlerAdapter
适配器的注册就比较随意
registerHttpRequestHandlerAdapter(parserContext, source);
registerSimpleControllerHandlerAdapter(parserContext, source);
2.3 HandlerExceptionResolvers 组件注册
默认采用 ExceptionHandlerExceptionResolver
(处理 @ExceptionHandler
方法注解)、ResponseStatusExceptionResolver
(处理 @ResponseStatus
类型、方法注解)、DefaultHandlerExceptionResolver
(处理普通的 Spring
异常) 作为异常处理类
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); RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class); statusExceptionResolver.setSource(source); statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); statusExceptionResolver.getPropertyValues().add("order", 1); String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver); RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class); defaultExceptionResolver.setSource(source); defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); defaultExceptionResolver.getPropertyValues().add("order", 2); String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);
2.4 其他的根据检测有效路径注册
static { ClassLoader classLoader = AnnotationDrivenBeanDefinitionParser.class.getClassLoader(); javaxValidationPresent = ClassUtils.isPresent("javax.validation.Validator", classLoader); romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader); jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader); jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader); jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader); jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader); gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader); }
后面会根据上面 Class 路径的检测结果注入相关的验证器和转换器。
下一章介绍 <mvc:annotation-driven>新增标签
转 : https://www.jianshu.com/p/fc3bc70b9ed3