抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SpringMVC-初始化工作

前言

​ 上一篇主要讲了tomcat启动SpringMVC的父子容器,以及一些配置项目的初始化工作,这一篇讲解下Spring的初始化工作

​ 一般启动SpringMVC需要使用@EnableWebMvc来开启SpringMVC

1
2
3
4
5
@Configuration
@EnableWebMvc
public class MyConfiguration {

}

EnableWebMvc

SpringMVC的入口,开启SpringMVC的注解

1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

看到这种注解,依据我们Spring的经验肯定看导入的DelegatingWebMvcConfiguration的类

DelegatingWebMvcConfiguration

概述

DelegatingWebMvcConfiguration是对Spring MVC进行配置的一个代理类。它结合缺省配置和用户配置定义Spring MVC运行时最终使用的配置。

DelegatingWebMvcConfiguration继承自WebMvcConfigurationSupport。而WebMvcConfigurationSupportSpring MVC提供缺省配置。这就是上面所提到的缺省配置。

​ DelegatingWebMvcConfiguration又会被注入一组WebMvcConfigurer,顾名思义,这是一组”Spring MVC配置器”。这组配置器由开发人员或者框架某一部分提供,是接口WebMvcConfigurer的实现类,按照Spring的建议,通常也是一个配置类,也就是使用了注解@Configuration的类。这组WebMvcConfigurer就是上面提到的用户配置。

DelegatingWebMvcConfiguration本身使用了注解@Configuration,所以它是一个配置类,因此它也会被作为一个单例bean注册到容器和被容器注入相应的依赖。WebMvcConfigurer注入到DelegatingWebMvcConfiguration正是因为他是一个bean并且在方法void setConfigurers(List<WebMvcConfigurer> configurers)使用了注解@Autowired(required = false)。

​ WebMvcConfigurationSupport提供缺省配置的主要方式是定义一组bean供Spring MVC在运行时使用,而这些bean定义方法所用的配置原料供应方法给子类保留了可覆盖的回调方法用于定制化(这些定制化回调方法正好通过接口WebMvcConfigurer抽象建模)。WebMvcConfigurationSupport为这些用于定制的回调方法提供了缺省实现(比如空方法),而DelegatingWebMvcConfiguration则覆盖了这些方法,使用所注入的WebMvcConfigurers对相应的配置原料进行定制从而起到定制整个Spring MVC配置的作用。

​ 注解@EnableWebMvc的作用其实就是引入一个DelegatingWebMvcConfiguration用于配置Spring MVC。而通常,我们正式使用注解@EnableWebMvc在某个实现了WebMvcConfigurer的配置类上来配置Spring MVC的,如下所示 :

1
2
3
4
5
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
// ...
}

继承关系

源代码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {


/**
* WebMvcConfigurerComposite 其实就是对多个 WebMvcConfigurer 的一个组合,
* 从命名就可以看出这一点
* WebMvcConfigurerComposite 自身也实现了接口 WebMvcConfigurer,
* 问 : 为什么要组合多个 WebMvcConfigurer 然后自己又实现该接口 ?
* 答 : 这么做的主要目的是在配置时简化逻辑。调用者对 WebMvcConfigurerComposite
* 可以当作一个 WebMvcConfigurer 来使用,而对它的每个方法的调用都又会传导到
* 它所包含的各个 WebMvcConfigurer 。
*/
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

/**
* 注入一组WebMvcConfigurer,这些WebMvcConfigurer由开发人员提供,或者框架其他部分提供,已经
* 以bean的方式注入到容器中
*
* @param configurers
*/
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
//添加MVC配置类
this.configurers.addWebMvcConfigurers(configurers);
}
}

/**
* 以下各个方法都是对WebMvcConfigurationSupport提供的配置原材料定制回调方法的覆盖实现,
* 对这些方法的调用最终都转化成了对configurers的方法调用,从而实现了定制化缺省Spring MVC
* 配置的作用
* @param configurer
*/
@Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
this.configurers.configurePathMatch(configurer);
}

@Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
this.configurers.configureContentNegotiation(configurer);
}

@Override
protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
this.configurers.configureAsyncSupport(configurer);
}

@Override
protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
this.configurers.configureDefaultServletHandling(configurer);
}

@Override
protected void addFormatters(FormatterRegistry registry) {
this.configurers.addFormatters(registry);
}

@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}

@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
this.configurers.addResourceHandlers(registry);
}

@Override
protected void addCorsMappings(CorsRegistry registry) {
this.configurers.addCorsMappings(registry);
}

@Override
protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);
}

@Override
protected void configureViewResolvers(ViewResolverRegistry registry) {
this.configurers.configureViewResolvers(registry);
}

@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
this.configurers.addArgumentResolvers(argumentResolvers);
}

@Override
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
this.configurers.addReturnValueHandlers(returnValueHandlers);
}

@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.configurers.configureMessageConverters(converters);
}

@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
this.configurers.extendMessageConverters(converters);
}

@Override
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
}

@Override
protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
this.configurers.extendHandlerExceptionResolvers(exceptionResolvers);
}

@Override
@Nullable
protected Validator getValidator() {
return this.configurers.getValidator();
}

@Override
@Nullable
protected MessageCodesResolver getMessageCodesResolver() {
return this.configurers.getMessageCodesResolver();
}
}

WebMvcConfigurationSupport

概述

​ WebMvcConfigurationSupport是对Spring MVC进行缺省配置的工具类,同时它自身也设计了供定制缺省配置的模板方法供子类覆盖用于对缺省配置进行定制。DelegatingWebMvcConfiguration继承自WebMvcConfigurationSupport,正是利用了WebMvcConfigurationSupport的这个扩展能力从而可以实现在缺省Spring MVC配置的基础上定制出最终使用的Spring MVC配置。

​ WebMvcConfigurationSupport的首要功能,是检测运行环境给出一套缺省配置。最终这套配置表现为一组注册到容器的bean。其次WebMvcConfigurationSupport在定义这些bean的过程中,调用了子类可以覆盖实现的配置原料提供方法,从而提供给了子类定制化Spring MVC配置的能力。

源码解析

类静态属性定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private static final 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;

private static final boolean jsonbPresent;

static {
ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
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);
jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
}
成员属性定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 当前类实现了接口ApplicationContextAware用于记录ApplicationContext,
// 该变量就是对应该接口用于记录ApplicationContext的变量
@Nullable
private ApplicationContext applicationContext;

// 当前类实现了接口ServletContextAware用于记录ServletContext,
// 该变量就是对应该接口用于记录ServletContext的变量
@Nullable
private ServletContext servletContext;

// 以下属性是Spring MVC 运行时需要的各种组件,也可以将它们理解成
// Spring MVC配置的各种原材料,当前实例(可能来自当前类或者子类)会根据
// 情况对这些原材料进行设置然后最终注册相应的bean到容器供Spring MVC运
// 行时使用。
@Nullable
private List<Object> interceptors;

//路径匹配器配置
@Nullable
private PathMatchConfigurer pathMatchConfigurer;

@Nullable
private ContentNegotiationManager contentNegotiationManager;

//参数解析器配置
@Nullable
private List<HandlerMethodArgumentResolver> argumentResolvers;

//返回值处理器定义
@Nullable
private List<HandlerMethodReturnValueHandler> returnValueHandlers;

//消息转换器配置
@Nullable
private List<HttpMessageConverter<?>> messageConverters;

//跨域请求配置
@Nullable
private Map<String, CorsConfiguration> corsConfigurations;

注册HandlerMapping

WebMvcConfigurationSupport#requestMappingHandlerMapping

创建RequestMappingHandlerMapping并完成配置交给Spring管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
//创建RequestMappingHandlerMapping
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
//设置拦截器列表
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
//设置跨域管理器
mapping.setCorsConfigurations(getCorsConfigurations());
//获取路径匹配器
PathMatchConfigurer configurer = getPathMatchConfigurer();
//是否使用后缀匹配
Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
//是否使用前缀匹配
Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
//是否使用下划线匹配
Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}

//获取URL辅助器
UrlPathHelper pathHelper = configurer.getUrlPathHelper();
if (pathHelper != null) {
mapping.setUrlPathHelper(pathHelper);
}
//获取路径匹配器
PathMatcher pathMatcher = configurer.getPathMatcher();
if (pathMatcher != null) {
mapping.setPathMatcher(pathMatcher);
}

Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
if (pathPrefixes != null) {
mapping.setPathPrefixes(pathPrefixes);
}

return mapping;
}
createRequestMappingHandlerMapping
1
2
3
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}

RequestMappingHandlerMapping

RequestMappingHandlerMapping这个类,是负责根据注解方式来处理HandlerMethod,那就来看下它的注册过程。名词解释

Handler : 绑定了注解@RequestMapping和@Controller的类

HandlerMethod:就是Handler下某个绑定@RequestMapping注解的方法(GetMapping、PostMapping…等都绑定的有注解@RequestMapping,spring mvc在做注解解析处理生成代理对象等的时候,会做值的合并等处理,所以最终都是用RequestMapping的注解来计算,所以@Controller和@RestController的处理等同)

为什么要注册HandlerMethod

如何根据注解信息和对应方法解析绑定在一起的,如下一段代码:

1
2
3
4
5
6
7
8
9
10

@RestController
@RequestMapping("/demo")
public class DemoController {

@RequestMapping(path = "/hello", method = RequestMethod.GET)
public String hello() {
return "hello, world";
}
}

​ spring mvc默认只有一个servlet映射/*请求,但是他可以根据请求信息:比如url:/demo/hello等匹配到hello()这个方法,这个过程是它的匹配HandlerMethod的过程,我们本文重点说的这个根据注解信息注册请求映射信息和对应的HandlerMethod的过程。

继承关系

afterPropertiesSet

AbstractHandlerMethodMapping这个类实现了接口InitializingBean(不清楚这个接口作用的同学,可以查下资料了解下),所以HandlerMethod的注册便在这个接口实现方法afterPropertiesSet()方法上了(这是因为spring 初始化bean的时候,如果某个bean实现了接口InitializingBean便会回调它的afterPropertiesSet()。

1
2
3
4
5
@Override
public void afterPropertiesSet() {
//初始化Mapping映射关系
initHandlerMethods();
}
initHandlerMethods

扫描Beans 初始化Mapping映射关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* 扫描Beans 初始化Mapping映射关系
*
* @see #getCandidateBeanNames()
* @see #processCandidateBean
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
//遍历Spring中所有的bean
// detectHandlerMethodsInAncestorContexts这个值 默认为true,
// 这样一来就会获取所有的bean名称,然后遍历进行过滤
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//处理候选Bean
processCandidateBean(beanName);
}
}
//空方法完成日志打印
handlerMethodsInitialized(getHandlerMethods());
}

afterPropertiesSet实现交给了initHandlerMethods,initHandlerMethods的执行流程如下:

  1. 得到所有的beanName
  2. 遍历beanNames,调用isHandler判断bean是不是一个controller,isHandler由子类实现,RequestMappingHandlerMapping的实现如下,判断bean中有没有Controller或RequestMapping
getCandidateBeanNames

获取所有Spring中的所有的Bean

1
2
3
4
5
6
protected String[] getCandidateBeanNames() {
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
//获取类型是Object的Bean
obtainApplicationContext().getBeanNamesForType(Object.class));
}
processCandidateBean

处理候选Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 处理候选的Bean
* @param beanName the name of the candidate bean
* @see #isHandler
* @see #detectHandlerMethods
* @since 5.1
*/
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
} catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
//2. isHandler由子类实现
//如果类上面有@Controller注解或者@RequestMapping注解
if (beanType != null && isHandler(beanType)) {
//3. 查找controller中有@RequestMapping注解的方法,并注册到请求
//建立uri和method的映射关系
detectHandlerMethods(beanName);
}
}
isHandler

如果类上面有@Controller注解或者@RequestMapping注解

1
2
3
4
5
6
7
8
9
10
/**
* 判断类是否有@Controller或者@RequestMapping的注解
*/
@Override
protected boolean isHandler(Class<?> beanType) {
//有@Controller注解
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
//或者有@RequestMapping
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

detectHandlerMethods是加载请求核心方法,执行流程如下:
(1) 得到controller真实类型,controller可能被代理
(2) 根据方法上的@RequestMapping信息构建RequestMappingInfo,由RequestMappingHandlerMapping实现。从代码上可以看出,必须方法上声明@RequestMapping,类上的@RequestMapping才会生效。

detectHandlerMethods

查找controller中有@RequestMapping注解的方法,并注册到请求容器中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* Look for handler methods in the specified handler bean.
* 查找controller中有@RequestMapping注解的方法,并注册到请求容器中
*
* @param handler either a bean name or an actual handler instance
* @see #getMappingForMethod
*/
protected void detectHandlerMethods(Object handler) {
//1. 得到controller真实类型
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());

if (handlerType != null) {
//取它的原生类型,比如是个代理类的时候,就获得被代理的类型
Class<?> userType = ClassUtils.getUserClass(handlerType);
//3. 封装所有的Method和RequestMappingInfo到Map中
//获取方法对象和方法上面的@RequestMapping注解属性封装对象的映射关系
//这个操作是把所有符合条件的method过滤出来, 这个泛型T:RequestMappingInfo
// 这个过滤方法的条件就是看方法上是否使用了注解RequestMapping,注意对RequestMapping
// 注解的解析并不是很简单的直接获取,因为可能使用了GetMapping等,所以会对它继承的注解解析并将属性值拼接起来
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
//2. 根据方法上的@RequestMapping信息构建RequestMappingInfo
// getMappingForMethod这个方法会根据method创建对应的请求映射信息返回,后面会重点说下这个方法
return getMappingForMethod(method, userType);
} catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
// 遍历过滤出来的所有Method和请求映射信息
methods.forEach((method, mapping) -> {
// 这里返回的是目标类型可调用的方法
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//4. 将RequestMappingInfo注册到请求容器中
//建立uri和方法的各种映射关系,反正一条,根据uri要能够找到method对象
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
selectMethods

扫描目标类的所有方法判断是否有@RequestMapper注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;

if (!Proxy.isProxyClass(targetType)) {
specificHandlerType = ClassUtils.getUserClass(targetType);
handlerTypes.add(specificHandlerType);
}
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

//循环currentHandlerType类的所有方法
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
//判断方法上面是否有@RequestMapping注解,如果有封装对象返回
//会调用匿名方法 getMappingForMethod
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
//建立方法对象和注解封装对象的映射关系
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}

return methodMap;
}

调用metadataLookup.inspect(specificMethod)方法会调用到getMappingForMethod方法中,完成@RequestMapping信息构建RequestMappingInfo的工作

getMappingForMethod

根据方法上的@RequestMapping信息构建RequestMappingInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//根据方法上的@RequestMapping创建RequestMappingInfo
//寻找有@RequestMapping注解的方法,然后注解里面的内容封装成对象
//这个创建方法可以看下面两个方法,代码比较直观易懂
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//2. 查找类上是否有@RequestMapping,如果有则和方法上的组合
//类上面的@RequestMapping注解也封装成对象
// 这个类型信息,是解析类上的RequestMapping注解信息,然后跟方法的进行拼接组合
// 比如方法上有路径/hello,类上:/demo,就可以拼接出/demo/hello,这里的拼接还包含其它信息的拼接,
//如果类上有两条路径,方法有两条,就会产生2*2=4条,多条路径类似
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
//把方法上面的注解属性结合到类上面的RequestMappingInfo对象中
// 注意这个方法的使用,是谁组合谁,谁在前
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
  1. 收集方法上面的@RequestMapping以及类上面的@RequestMapping注解
  2. 将方法和类的RequestMappingInfo进行合并
  3. 封装所有的RequestMappingInfo到Map中
  4. 将RequestMappingInfo注册到请求容器中
registerHandlerMethod

注册HandlerMethod和它唯一映射信息

1
2
3
4
5
6
7
8
9
/**
* Register a handler method and its unique mapping. Invoked at startup for
* each detected handler method.
* 注册HandlerMethod和它唯一映射信息
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
// mappingRegistry是它的一个内部类的实例
this.mappingRegistry.register(mapping, handler, method);
}
register
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
this.readWriteLock.writeLock().lock();
try {
// 这里会创建一个HandlerMethod实例
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 如果mappingLookup已经存在mapping信息这里会抛出异常
validateMethodMapping(handlerMethod, mapping);
//建立uri对象和handlerMethod的映射关系
// 这是一个map,保存了Key: RequestMappingInfo和Value: HandlerMethod
this.mappingLookup.put(mapping, handlerMethod);
// 一个mapping是允许存在多条路径的
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
//建立url和RequestMappingInfo映射关系
//urlLookup是个LinkedMultiValueMap,也就是说一个路径可以有多个mapping
// 在进行匹配的时候,就是先根据url找到合适的mapping,然后根据找到的mapping再去找到HandlerMethod
this.urlLookup.add(url, mapping);
}

String name = null;
if (getNamingStrategy() != null) {
// 大写类名#方法名
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}

//判断method上是否有CrossOrigin注解,把注解里面的属性封装成CorsConfiguration,这个是做跨域访问控制的
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
//建立映射关系
this.corsLookup.put(handlerMethod, corsConfig);
}

this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
} finally {
this.readWriteLock.writeLock().unlock();
}
}

主要完成对URI以及调用方法的映射,这样RequestMapping的整个初始化工作就完成了。

注册HandlerAdapter

RequestMappingHandlerAdapter请求映射处理适配器,在SpringMVC中它是一个非常重要的类,对请求处理方法的调用主要是通过这个类来完成的(这段代码mv = ha.handle(processedRequest, response, mappedHandler.getHandler());)。

WebMvcConfigurationSupport#requestMappingHandlerAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
//创建RequestMappingHandlerAdapter
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(contentNegotiationManager);
//设置消息转换器
adapter.setMessageConverters(getMessageConverters());
//设置web绑定器
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
//设置自定义参数转换器 通过钩子方法获取
adapter.setCustomArgumentResolvers(getArgumentResolvers());
//设置自定义返回参数处理器 通过钩子方法获取
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

if (jackson2Present) {
adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
}

AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
configureAsyncSupport(configurer);
if (configurer.getTaskExecutor() != null) {
adapter.setTaskExecutor(configurer.getTaskExecutor());
}
if (configurer.getTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getTimeout());
}
//设置拦截器回调
adapter.setCallableInterceptors(configurer.getCallableInterceptors());
//设置默认结果拦截器
adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

return adapter;
}

RequestMappingHandlerAdapter

RequestMappingHandlerAdapter实现了HandlerAdapter接口,顾名思义,表示handler的adapter,这里的handler指的是Spring处理具体请求的某个Controller的方法,也就是说HandlerAdapter指的是将当前请求适配到某个Handler的处理器。RequestMappingHandlerAdapter是HandlerAdapter的一个具体实现,主要用于将某个请求适配给@RequestMapping类型的Handler处理。

继承关系

构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 构造方法
*/
public RequestMappingHandlerAdapter() {
this.messageConverters = new ArrayList<>(4);
//添加消息转换器
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

从上面的代码中我们可以看到这里默认添加了四种类型的Http数据转换器。其中我们需要关注的时候AllEncompassingFormHttpMessageConverter这个转换器,我们也去它的构造函数中看一下:

AllEncompassingFormHttpMessageConverter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public AllEncompassingFormHttpMessageConverter() {
try {
addPartConverter(new SourceHttpMessageConverter<>());
} catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}

//如果Classpath下面有javax.xml.bind.Binder类,
//没有com.fasterxml.jackson.dataformat.xml.XmlMapper类的话
//则添加Jaxb2RootElementHttpMessageConverter转换器
if (jaxb2Present && !jackson2XmlPresent) {
addPartConverter(new Jaxb2RootElementHttpMessageConverter());
}

//如果Classpath下有com.fasterxml.jackson.databind.ObjectMapper
//和com.fasterxml.jackson.core.JsonGenerator的话,则添加
//MappingJackson2HttpMessageConverter转换器
if (jackson2Present) {
addPartConverter(new MappingJackson2HttpMessageConverter());
}
//如果Classpath下面有com.google.gson.Gson类的话,则添加
//GsonHttpMessageConverter转换器
else if (gsonPresent) {
addPartConverter(new GsonHttpMessageConverter());
}
//如果Classpath下面有javax.json.bind.Jsonb类的话,则添加
//JsonbHttpMessageConverter转换器
else if (jsonbPresent) {
addPartConverter(new JsonbHttpMessageConverter());
}
//如果Classpath下有com.fasterxml.jackson.dataformat.xml.XmlMapper类的话,
//则添加MappingJackson2XmlHttpMessageConverter转换器

if (jackson2XmlPresent) {
addPartConverter(new MappingJackson2XmlHttpMessageConverter());
}
//如果Classpath下有com.fasterxml.jackson.dataformat.smile.SmileFactory类的话,
//则添加MappingJackson2SmileHttpMessageConverter转换器
if (jackson2SmilePresent) {
addPartConverter(new MappingJackson2SmileHttpMessageConverter());
}
}
FormHttpMessageConverter

其实在它的父类中还添加了三种类型的Convert

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public FormHttpMessageConverter() {
//application/x-www-form-urlencoded
this.supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
//multipart/form-data
this.supportedMediaTypes.add(MediaType.MULTIPART_FORM_DATA);
//ByteArrayHttpMessageConverter
this.supportedMediaTypes.add(MediaType.MULTIPART_MIXED);

this.partConverters.add(new ByteArrayHttpMessageConverter());
//StringHttpMessageConverter
this.partConverters.add(new StringHttpMessageConverter());
//ResourceHttpMessageConverter
this.partConverters.add(new ResourceHttpMessageConverter());

applyDefaultCharset();
}
afterPropertiesSet

RequestMappingHandlerAdapter实现了InitializingBean,spring启动完成后就会调用afterPropertiesSet方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
//初始化ControllerAdvice缓存
initControllerAdviceCache();

//处理参数解析器
if (this.argumentResolvers == null) {
//获取默认的参数解析器
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
//封装到HandlerMethodArgumentResolverComposite中
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//处理参数绑定解析器
if (this.initBinderArgumentResolvers == null) {
//获取默认的参数绑定解析器
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
//封装到HandlerMethodArgumentResolverComposite
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//处理返回参数处理器
if (this.returnValueHandlers == null) {
//获取默认的返回参数处理器
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
//封装到HandlerMethodArgumentResolverComposite
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
initControllerAdviceCache

在这个方法里主要是获取了带ControllerAdvice注解的类,并从这些类中查找实现了RequestBodyAdvice或者ResponseBodyAdvice接口的类,添加到requestResponseBodyAdvice集合中,另外获取所有带ModelAttribute注解且没有RequestMapping注解的方法,放到modelAttributeAdviceCache集合中,获取所有带InitBinder注解的方法放到initBinderAdviceCache的集合中。下面我们再看这一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}

//获取所有带@ControllerAdvice注解的类
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

//循环获取到的ControllerAdviceBean
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
//获取所有ModelAttribute注解的方法,并且没有RequestMapping注解的方法
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}

//获取所有带InitBinder注解的方法
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
//如果实现了RequestBodyAdvice接口或者实现了ResponseBodyAdvice接口
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
//添加到requestResponseBodyAdvice集合中
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}

if (logger.isDebugEnabled()) {
int modelSize = this.modelAttributeAdviceCache.size();
int binderSize = this.initBinderAdviceCache.size();
int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);
int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);
if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
logger.debug("ControllerAdvice beans: none");
}
else {
logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
}
}
}
getDefaultArgumentResolvers

这里我们是没有手工配置RequestMappingHandlerAdapter的,所以,会先调用getDefaultArgumentResolvers方法,获取一系列默认的HandlerMethodArgumentResolver的实现类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());

// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}

// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));

return resolvers;
}

SpringMVC为我们自动添加了大概26个HandlerMethodArgumentResolver的实现类。这里有一个方法需要我们注意一下:getCustomArgumentResolvers() 方法。这个方法是用来获取自定义的HandlerMethodArgumentResolver的实现类,也就是说如果我们有自己写的HandlerMethodArgumentResolver的方法的话,我们配置的属性是customArgumentResolvers,最好不要配置argumentResolvers这个属性。然后我们注意的一点是我们所添加的所以的HandlerMethodArgumentResolver的实现类,都是添加到了HandlerMethodArgumentResolverComposite这个类中,HandlerMethodArgumentResolverComposite也是HandlerMethodArgumentResolver的一个实现类。还有一点需要注意的是,这里创建了两个RequestParamMethodArgumentResolver的实例,一个传了true参数,一个传了false参数,这是用来解析不同的请求参数的。

总结

​ 讲述了SpringMVC使用@EnableWebMvc 启动SpringMVC后首先导入DelegatingWebMvcConfiguration类,然后从父类的WebMvcConfigurationSupport通过@Bean的方式注册HandlerMapping以及HandlerAdapter,其他的还有handlerExceptionResolver异常处理器注册,mvcViewResolver视图解析器的注册等等。

评论