SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器
mvc:annotation-driven节点的解析器,是springmvc的核心解析器
官方注释
Open Declaration org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser
A BeanDefinitionParser that provides the configuration for the <annotation-driven/> MVC namespace element.
This class registers the following <<HandlerMappings>>:
•RequestMappingHandlerMapping ordered at 0 for mapping requests to annotated controller methods.
•BeanNameUrlHandlerMapping ordered at 2 to map URL paths to controller bean names.
Note: Additional HandlerMappings may be registered as a result of using the <view-controller> or the <resources> MVC namespace elements.
This class registers the following <<HandlerAdapters>>:
•RequestMappingHandlerAdapter for processing requests with annotated controller methods.
•HttpRequestHandlerAdapter for processing requests with HttpRequestHandlers.
•SimpleControllerHandlerAdapter for processing requests with interface-based Controllers.
This class registers the following <<HandlerExceptionResolvers>>:
•ExceptionHandlerExceptionResolver for handling exceptions through @ExceptionHandler methods.
•ResponseStatusExceptionResolver for exceptions annotated with @ResponseStatus.
•DefaultHandlerExceptionResolver for resolving known Spring exception types
This class registers an org.springframework.util.<<AntPathMatcher>> and a org.springframework.web.util.<<UrlPathHelper>> to be used by:
•the RequestMappingHandlerMapping,
•the HandlerMapping for ViewControllers
•and the HandlerMapping for serving resources
Note that those beans can be configured by using the path-matching MVC namespace element.
Both the RequestMappingHandlerAdapter and the ExceptionHandlerExceptionResolver are configured with instances of the following by default:
•A ContentNegotiationManager
•A DefaultFormattingConversionService
•A org.springframework.validation.beanvalidation.LocalValidatorFactoryBean if a JSR-303 implementation is available on the classpath
•A range of HttpMessageConverters depending on what 3rd party libraries are available on the classpath.
主要注册HandlerMappings-路径匹配器、HandlerAdapters-路径匹配适配器、HandlerExceptionResolvers-异常解析器、AntPathMatcher-路径解析器、UrlPathHelper-请求路径获取帮助类等beans,提前为mvc做好基础的准备
AnnotationDrivenBeanDefinitionParser#parse
从此方法中,提炼实现的最主要代码片段,代码如下
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
//格式转换处理拦截类,比如时间、数字等
parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
跟官方注释提及的一样,主要目的是注册与mvc处理有关的相关beans以及默认的mvc组件,下面我们将从以上代码的不同组件生成进行逐个分析
HandlerMappings组件注册
对应parse()方法的代码片段如下
//生成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);
//查看mvc:annotation-driven有无enable-matrix-variables/enableMatrixVariables,表示是否开启多变量映射比如/cars;a=1;b=1
//具体使用可查阅相关文档,默认removeSemicolonContent为false
if (element.hasAttribute("enable-matrix-variables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
else if (element.hasAttribute("enableMatrixVariables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
//配置路径匹配解析器等属性
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
//将RequestMappingHandlerMapping注册为bean对象放置bean工厂中 readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
RequestMappingHandlerMapping主要是处理@Controller和@RequestMapping注解的,另外再看下configurePathMatchingProperties()配置路径解析器方法
AnnotationDrivenBeanDefinitionParser#configurePathMatchingProperties-路径匹配解析器配置
代码如下
private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, Element element,
ParserContext parserContext) {
//获取mvc:annotation-driven下子节点mvc:path-matching
Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching");
if (pathMatchingElement != null) {
Object source = parserContext.extractSource(element);
//是否采用suffix-pattern,即.*,比如/user也匹配/user.*。默认为true
if (pathMatchingElement.hasAttribute("suffix-pattern")) {
Boolean useSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern"));
handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch);
}
//是否采用分隔符,特指/,比如/user也匹配/user/。默认为true
if (pathMatchingElement.hasAttribute("trailing-slash")) {
Boolean useTrailingSlashMatch = Boolean.valueOf(pathMatchingElement.getAttribute("trailing-slash"));
handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useTrailingSlashMatch);
}
//是否采用contentNegotiationManager中的格式,比如*.json/*.xml。默认为false
if (pathMatchingElement.hasAttribute("registered-suffixes-only")) {
Boolean useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only"));
handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch);
}
RuntimeBeanReference pathHelperRef = null;
//路径解析帮助类,可指定,默认为UrlPathHelper
if (pathMatchingElement.hasAttribute("path-helper")) {
pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper"));
}
pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, parserContext, source);
handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef);
RuntimeBeanReference pathMatcherRef = null;
//路径解析器,默认为AntPathMatcher解析器
if (pathMatchingElement.hasAttribute("path-matcher")) {
pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher"));
}
pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, parserContext, source);
handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
}
}
看代码逻辑,主要是通过mvc:annotation-driven下的mvc:path-matching节点来配置
- suffix-pattern-是否启用后缀匹配,默认为true,即对指定的url会再新增
.*,比如/user实际匹配/user.*->/user匹配/user.html,/user.jsp- trailing-slash-是否启动尾部斜线匹配,默认为true,即对指定的url会新增
/,比如/user也会匹配/user/- registered-suffixes-only-是否启用media type类型的匹配,即对指定的url新增
*.json/*.xml等匹配,默认为false- path-helper-路径获取帮助类,默认为UrlPathHelper类
- path-matcher-路径匹配解析器,默认为AntPathMather
HandlerAdapters组件注册
相应的代码片段如下
//获取conversion-service属性,默认为FormattingConversionServiceFactoryBean
//处理一些基本类的格式与转换,比如时间、数字等
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
//获取validator,默认为OptionalValidatorFactoryBean,基于java自带的Validator接口,表示校验器,可用于javabean的参数校验等
RuntimeBeanReference validator = getValidator(element, source, parserContext);
//获取message-codes-resolver属性,默认为null。
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
//设置ConfigurableWebBindingInitializer类 用于数据绑定
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子节点的配置,消息转换器,可用于向前端发送数据再次自定义组装
//比如MappingJackson2HttpMessageConverter json转字符串
ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
//argument-resolvers子节点配置,参数解析
ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);
//return-value-handlers子节点解析
ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);
//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, parserContext);
ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);
//将上述的属性添加到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);
//ignoreDefaultModelOnRedirect属性配置
if (element.hasAttribute("ignore-default-model-on-redirect")) {
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
// "ignoreDefaultModelOnRedirect" spelling is deprecated
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
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);
配置的主要是
RequestMappingHandlerAdapter对象,其中的属性较多,读者可查看>>>SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器。其主要支持HandlerMethod类,而这些类都是由RequestMappingHandlerMapping来生成的,而这又是通过AbstractHandlerMethodMapping#initHandlerMethods来实现的,有兴趣的可以自行去查阅
ExceptionResolver组件注册
默认采用ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver作为异常处理类
RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
exceptionHandlerExceptionResolver.setSource(source);
exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
addResponseBodyAdvice(exceptionHandlerExceptionResolver);
String methodExceptionResolverName = readerContext.registerWithGeneratedName(exceptionHandlerExceptionResolver);
RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
responseStatusExceptionResolver.setSource(source);
responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
responseStatusExceptionResolver.getPropertyValues().add("order", 1);
String responseStatusExceptionResolverName =
readerContext.registerWithGeneratedName(responseStatusExceptionResolver);
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
defaultExceptionResolver.getPropertyValues().add("order", 2);
String defaultExceptionResolverName =
readerContext.registerWithGeneratedName(defaultExceptionResolver);
ExceptionHandlerExceptionResolver-处理
@ExceptionHandler方法注解ResponseStatusExceptionResolver-处理
@ResponseStatus类型、方法注解DefaultHandlerExceptionResolver-处理普通的spring异常
更准确的可查看官方注释说明
小结
mvc:annotation-driven一句话便囊括了springmvc的基本处理组件注册成bean到springmvc上下文,结合此处再去理解springmvc的逻辑源码就简单了
SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器的更多相关文章
- Spring源码情操陶冶#task:scheduled-tasks解析器
承接前文Spring源码情操陶冶#task:executor解析器,在前文基础上解析我们常用的spring中的定时任务的节点配置.备注:此文建立在spring的4.2.3.RELEASE版本 附例 S ...
- Spring源码情操陶冶-tx:advice解析器
承接Spring源码情操陶冶-自定义节点的解析.本节关于事务进行简单的解析 spring配置文件样例 简单的事务配置,对save/delete开头的方法加事务,get/find开头的设置为不加事务只读 ...
- Spring源码情操陶冶#task:executor解析器
承接Spring源码情操陶冶-自定义节点的解析.线程池是jdk的一个很重要的概念,在很多的场景都会应用到,多用于处理多任务的并发处理,此处借由spring整合jdk的cocurrent包的方式来进行深 ...
- SpringMVC源码情操陶冶-ViewResolver视图解析
简单分析springmvc是如何解析view视图,并返回页面给前端 SpringMVC配置视图解析器 <bean id="viewResolver" class=" ...
- SpringMVC源码情操陶冶-DispatcherServlet
本文对springmvc核心类DispatcherServlet作下简单的向导,方便博主与读者查阅 DispatcherServlet-继承关系 分析DispatcherServlet的继承关系以及主 ...
- SpringMVC源码情操陶冶-AbstractHandlerMethodMapping
承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,本文将介绍如何注册HandlerMethod对象作为handler 类结构瞧一瞧 public abstract ...
- SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器
承接前文SpringMVC源码情操陶冶-HandlerAdapter适配器简析.RequestMappingHandlerAdapter适配器组件是专门处理RequestMappingHandlerM ...
- SpringMVC源码情操陶冶-DispatcherServlet简析(二)
承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ...
- SpringMVC源码情操陶冶-FreeMarker之web配置
前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...
随机推荐
- 哈密顿绕行世界问题(dfs+记录路径)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2181 哈密顿绕行世界问题 Time Limit: 3000/1000 MS (Java/Others) ...
- MySQL的ibdata1文件占用过大
处理MySQL的ibdata1文件过大问题 本人遇到一次在安装zabbix监控的时候,yum安装的MySQL数据库,后面用了一段时间发现data目录下的ibdata1的空间特别大,反而我的zabbix ...
- c++(递归和堆栈)
看过我前面博客的朋友都清楚,函数调用主要依靠ebp和esp的堆栈互动来实现的.那么递归呢,最主要的特色就是函数自己调用自己.如果一个函数调用的是自己本身,那么这个函数就是递归函数. 我们可以看一下普通 ...
- 解决get乱码
也可以在tomcat中修改,但是每次配置tomcat中都得修改.容易忘记,还是图片这个方法最好,推荐
- slice、splice与split傻傻分不清
每每看到这几个,就蒙圈了,这都是啥呀? 既然这么容易混淆,我还是来做个小笔记吧,以便日后查阅: 1.slice(数组) 定义:slice() 方法可从已有的数组中返回选定的元素. 用法:array ...
- 【学习笔记】js下拉刷新、上拉加载 mescroll框架的使用
写在前边: 工作需要,使用ajax在原来的列表下边使用ajax请求后台数据,拼接在列表最下边,在github转了好久,发现了一个bug极多的js刷新插件,尝试了一个下午,就在快放弃的时候,发现下边有留 ...
- k8s 创建资源的两种方式 - 每天5分钟玩转 Docker 容器技术(124)
命令 vs 配置文件 Kubernetes 支持两种方式创建资源: 1. 用 kubectl 命令直接创建,比如: kubectl run nginx-deployment --image=nginx ...
- Tp-link路由器怎么设置端口映射 内网端口映射听语音
https://jingyan.baidu.com/article/ca00d56c710ef9e99eebcf85.html 只有一台能上网的电脑就可以自己免费搭建服务器,本经验简单介绍家用tp-l ...
- kafka和strom集群的环境安装
前言 storm和kafka集群安装是没有必然联系的,我将这两个写在一起,是因为他们都是由zookeeper进行管理的,也都依赖于JDK的环境,为了不重复再写一遍配置,所以我将这两个写在一起.若只需一 ...
- Java调用阿里云短信通道服务【千锋】
这里我们使用SpringBoot 来调用阿里通信的服务. 阿里通信,双11.收到短信,日发送达6亿条.保障力度非常高. 使用的步骤: 1.1. 第一步:需要开通账户 1.2. 第二步:阅读接口文档 1 ...