Spring-MVC注解
1. 前言
- 通过查看 spring-webmvc-xxx.jar 下的spring.handlers文件可以发现 mvc前缀的标签都是由
MvcNamespaceHandler
来进行解析的。 - 通过查看其内部唯一的方法
init()
的实现可以确定<mvc:annotation-driven />
的解析工作是由AnnotationDrivenBeanDefinitionParser
类全权负责的。
2. AnnotationDrivenBeanDefinitionParser
其所实现的接口
BeanDefinitionParser
的作用和意义就不再赘述了.
观察该类对所继承接口的实现可以发现:
- 向SpringMVC容器中注册了
ContentNegotiationManagerFactoryBean
- 向SpringMVC容器中注册了
RequestMappingHandlerMapping
(间接实现了HandlerMapping
接口), order为0 - 向SpringMVC容器中注册了
RequestMappingHandlerAdapter
(间接实现了HandlerAdapter
接口,直接实现了InitializingBean
接口,关于这个接口的实现,参见本人的另外一篇博客) - 向SpringMVC容器中注册了
MappedInterceptor
- 向SpringMVC容器中注册了
ExceptionHandlerExceptionResolver
(间接实现了HandlerExceptionResolver
接口), order为0 - 向SpringMVC容器中注册了
ResponseStatusExceptionResolver
(间接实现了HandlerExceptionResolver
接口), order为1 - 向SpringMVC容器中注册了
DefaultHandlerExceptionResolver
(间接实现了HandlerExceptionResolver
接口), order为2
2.1 日志分析
以下是从启动日志里提取出来的, 为了注释特意进行了换行, 原本只是一行.
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@d672d2: // SpringMVC容器
defining beans [
mvcContentNegotiationManager, // 在AnnotationDrivenBeanDefinitionParser类的getContentNegotiationManager()方法中进行注册
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0, //
org.springframework.format.support.FormattingConversionServiceFactoryBean#0, // 在AnnotationDrivenBeanDefinitionParser类的getConversionService()方法中进行注册
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0, //
org.springframework.web.servlet.handler.MappedInterceptor#0, //
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0, //
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0, //
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0, // 以上可以参见上面的解释
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter, //
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter, // 以上三个由MvcNamespaceUtils.registerDefaultComponents()方法完成
org.springframework.aop.config.internalAutoProxyCreator, //
loginController, // 自定义
userController, // 自定义
org.springframework.context.annotation.internalConfigurationAnnotationProcessor,
org.springframework.context.annotation.internalAutowiredAnnotationProcessor,
org.springframework.context.annotation.internalRequiredAnnotationProcessor,
org.springframework.context.annotation.internalCommonAnnotationProcessor,
org.springframework.web.servlet.resource.ResourceHttpRequestHandler#0, // 在spring-mvc.xml中的<mvc:resources />
viewResolver, // 在spring-mvc.xml中注册
];
parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@f80021
3. 绑定Handler与Method
信息: Mapped “{[//logout],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}” onto public void cn.springmvc.controller.LoginController.logout(javax.servlet.http.HttpServletResponse,javax.servlet.http.HttpSession)
以上这一句日志信息属于INFO级别; 位置位于 RequestMappingHandlerMapping
类的祖先类AbstractHandlerMethodMapping<T>
内的registerHandlerMethod
方法中.
我们仔细查看RequestMappingHandlerMapping
类的继承链就会发现, 其祖先类 AbstractHandlerMethodMapping<T>
实现了 InitializingBean
, 如此有名的接口我就不多介绍了. 现在就让我们直接查看AbstractHandlerMethodMapping<T>
对该接口的实现吧.
/**
* Detects handler methods at initialization.
*/
public void afterPropertiesSet() {
initHandlerMethods();
}
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #isHandler(Class)
* @see #getMappingForMethod(Method, Class)
* @see #handlerMethodsInitialized(Map)
*/
protected void initHandlerMethods() {
// this.detectHandlerMethodsInAncestorContexts默认为false
// getApplicationContext()获取到的是SpringMVC容器
// 注意我们在spring-mvc.xml中除了键入<mvc:annotation-driven />之外,
// 还会键入<context:component-scan base-package="xx.yyy.zzz" />;关于这个自定义标签, 请参见本人的上一篇博客
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
// isHandler在AbstractHandlerMethodMapping<T>类中抽象方法;
// RequestMappingHandlerMapping对其的实现请看下面
if (isHandler(getApplicationContext().getType(beanName))){
// 查看下面的讲解
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
3.1 isHandler
RequestMappingHandlerMapping
类对祖先类AbstractHandlerMethodMapping<T>
类的实现
// 判断该Bean是否属于Handler,
// 关于Controller注解, 其被@Component修饰着, 更多的请参见本人的上一篇博客
// 所以这里默认是筛选出所有的我们注册进SpringMVC容器中的标记了@Controller的Bean(例如使用<context:component-scan/>)
protected boolean isHandler(Class<?> beanType) {
return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
3.2 detectHandlerMethods
具体实现位于AbstractHandlerMethodMapping<T>
类中, 详细代码就不贴了. 我们只看其中的重点内容
getMappingForMethod(method, userType)
其中:
- userType为我们自定义的, 被@Controller注解所修饰的类.
- method为
java.lang.reflect.Method
类型, 为我们所自定义的userType类型中的方法. - 该
getMappingForMethod
方法也是抽象的, 交由RequestMappingHandlerMapping
类来实现.
registerHandlerMethod(handler, method, mappings.get(method));
其中:
- handler为传入的bean name
- method为
java.lang.reflect.Method
类型, 为我们所自定义的userType类型中的方法. - mappings.get(method)为我们使用
getMappingForMethod
构造的RequestMappingInfo
实例
3.3 getMappingForMethod
由RequestMappingHandlerMapping
类进行实现
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
// 抽取该方法上注解的@RequestMapping
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
// getCustomMethodCondition默认返回null
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
// 抽取handlerType(即我们自定义的Controller类)上注解的@RequestMapping
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
// 如果该方法所在的类也有@RequestMapping信息, 则进行合并
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
// 合并两个RequestMapping的信息
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
3.4 createRequestMappingInfo
具体实现位于RequestMappingHandlerMapping
类
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, RequestCondition<?> customCondition) {
return RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) /* 这里说明我们是可以在配置@RequestMapping时, 使用spel表达式的; 例如@RequestMapping("/yuyue/${url.mh}/login") */
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name())
.customCondition(customCondition)
.options(this.config)
.build();
}
3.5 registerHandlerMethod
对上面抽取出来RequestMappingInfo
信息 注册到 LinkedHashMap<RequestMappingInfo,HandlerMethod>
类型的全局字段handlerMethods
和全局字段LinkedMultiValueMap<String, RequestMappingInfo>
类型的全局字段urlMap中.
4. 总结
所以SpringMVC对的读取可以归纳如下: (2018/1/24新增)
<context:component-scan/>
默认是将所有@Component注解的类扫描进容器; @Controller自身就是被@Component修饰的<mvc:annotation-driven />
的解析工作是由 AnnotationDrivenBeanDefinitionParser 类全权负责的。AnnotationDrivenBeanDefinitionParser
会向SpringMVC容器中注册了RequestMappingHandlerMapping
(间接实现了HandlerMapping接口), order为0- 查看
RequestMappingHandlerMapping
类的继承链就会发现, 其祖先类AbstractHandlerMethodMapping<T>
实现了InitializingBean
- 而对
InitializingBean
的实现中, 会回调RequestMappingHandlerMapping.isHandler
的实现,在那里你就可以看到对@RequestMapping
注解的扫描了.
5. 引用
依然是CSDN推送的文章