1.组件概览

这里的组件指的是DispatcherServlet中直接初始化的那九个组件,不同的组件内部还会用到一些子组件。

1.1HandlerMapping

作用:根据request找到相应的处理器Handler和Interceptors。

可以实现Order接口以控制遍历HandlerMapping的顺序,越小越先使用优先级越高。

1
2
3
4
5
6
7
public interface HandlerMapping {
default boolean usesPathPatterns() {
return false;
}
//1.根据请求获取HandlerExecutionChain
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

1.2HandlerAdapter

作用:调用handler来真正处理请求。

1
2
3
4
5
6
7
8
public interface HandlerAdapter {
//1.根据handler判断本对象是否支持使用该handler处理
boolean supports(Object handler);
//2.使用handler进行处理
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
//3.获取资源的Last-Modified(资源最后一次的修改时间)
long getLastModified(HttpServletRequest request, Object handler);
}

视图名称是handler处理器返回的,或者RequestToViewNameTranslator获取的默认视图名。

1.3HandlerExceptionResolver

作用:请求处理过程出现异常后,由此组件进行处理。只用于解析对请求做处理过程中的异常,而渲染环节产生的异常不归它管。

1
2
3
4
5
6
public interface HandlerExceptionResolver {
//根据异常解析处理异常
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

1.4ViewResolver

作用:将String类型的视图名和Locale解析为View类型的视图,找到渲染所需要的模板和所用技术(也就是视图类型)。

1
2
3
4
5
public interface ViewResolver {
//根据视图名称解析视图对象
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}

最常使用的UrlBasedViewResolver系列的解析器都是针对单一视图类型进行解析的,如FreeMarkerViewResolver、InternalResourceViewResolver、VelocityViewResolver、ThymeleafViewResolver。

ResourceBundleViewResolver、XmlViewResolver、BeanNameResolver可以同时解析多种类型的视图。ResourceBundleViewResolver根据properties文件解析出视图,文件中指定视图类型和视图地址;XmlViewResolver根据xml文件解析出视图;BeanNameResolver是根据viewName从ApplicationContext容器中查找相应的bean做为View。

1.5RequestToViewNameTranslator

作用:处理返回的ModelAndVIew对象中即没有view也没有设置viewName的情况,这时需要从request获取viewName。即定义了如何从请求对象获取viewName。

1
2
3
4
public interface RequestToViewNameTranslator {
@Nullable
String getViewName(HttpServletRequest request) throws Exception;
}

1.6LocaleResolver

作用:从request中解析出Locale。

1
2
3
4
5
6
public interface LocaleResolver {
//从请求获取Locale
Locale resolveLocale(HttpServletRequest request);
//设置请求和响应Locale
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}

Spring MVC中主要在两个地方用到了Locale:1.ViewResolver解析视图的时候2.使用到国际化资源或者主题的时候。

我们可以通过MVC拦截器 LocaleChangeInterceptor 来拦截请求的请求参数,从而设置改变请求的Locale属性。比如http://localhost:8080?locale=zh_cn就会将请求的Locale设置为中文。

1.7ThemeResolver

作用:根据请求解析主题名称。

1
2
3
4
5
6
public interface ThemeResolver {
//解析主题
String resolveThemeName(HttpServletRequest request);
//设置主题
void setThemeName(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable String themeName);
}

Spring MVC中一套主题对应一个properties文件,里面存放着跟当前主题相关的所有资源,如图片、css样式等。例如:

image-20240321160414104

ThemeResolver的作用是从request解析出主题名;ThemeSource则是根据主题名找到具体的主题;Theme是ThemeSource找出的一个具体的主题,可以通过它获取主题里面的资源。

获取主题资源是在RequestContext中。

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
//RequestContext.java
public Theme getTheme() {
if (this.theme == null) {
// Lazily determine theme to use for this RequestContext.
this.theme = RequestContextUtils.getTheme(this.request);
if (this.theme == null) {
// No ThemeResolver and ThemeSource available -> try fallback.
this.theme = getFallbackTheme();
}
}
return this.theme;
}

//RequestContextUtils.java
public static Theme getTheme(HttpServletRequest request) {
//1.根据请求获取ThemeResolver(数据源于IOC容器)
ThemeResolver themeResolver = getThemeResolver(request);
//2.根据请求获取ThemeSource
ThemeSource themeSource = getThemeSource(request);
if (themeResolver != null && themeSource != null) {
//3.themeResolver根据请求解析主题名称
String themeName = themeResolver.resolveThemeName(request);
//4.themeSource根据主题名称解析主题对象
return themeSource.getTheme(themeName);
}
else {
return null;
}
}

ThemeResolver的默认实现是 FixedThemeResolver,这里边使用固定的默认主题”theme”。

ThemeSource的默认实现是WebApplicationContext的实现类。默认底层实现是ResourceBundleThemeSource,即WebApplicationContext封装了themesource属性的实现类型为ResourceBundleThemeSource,具体创建过程在UiApplicationContextUtils的initThemeSource方法

1
2
//直接调用构造器创建,且设置到WebApplicationContext的themesource属性
themeSource = new ResourceBundleThemeSource();

Spring MVC中主题切换和Locale的切换使用相同的模式,也是使用Interceptor,即ThemeChangeInterceptor

1.8MultipartResolver

作用:用于处理上传请求,处理方法是将普通request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取到File,或多文件getFileMap。

1
2
3
4
5
6
7
8
public interface MultipartResolver {
//1.判断是不是上传请求
boolean isMultipart(HttpServletRequest request);
//2.将request包装成MultipartHttpServletRequest
MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;
//3.处理完后清理上传过程中产生的临时资源
void cleanupMultipart(MultipartHttpServletRequest request);
}

1.9FlashMapManager

作用:管理FlashMap。

FlashMap主要用在redirect中传递参数。

1
2
3
4
5
6
public interface FlashMapManager {
//获取input flashmap
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
//设置output flashmap
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}

retrieveAndUpdate方法用于恢复参数,并将恢复过的和超时的参数从保存介质中删除;saveOutputFlashMap用于将参数保存起来。

默认实现是 SessionFlashMapManager ,它是将参数保存到session中。

整个redirect的参数通过FlashMap的传递过程分为三步

1.在处理器中将需要传递的参数设置到outputFlashMap中。当处理器处理完请求时,如果是redirect类型的返回值RequestMappingHandlerAdapter会将其设置到outputFlashMap中。即adapter的getModelAndView方法中实现。

2.在RedirectView的renderMergedOutputModel方法中调用FlashMapManager的saveOutputFlashMap方法,将outputFlashMap中的参数设置到Session中。

3.请求redirect后DispatcherServlet的doService会调用FlashMapManager的retrieveAndUpdate方法从Session中获取inputFlashMap并设置到Request的属性中备用,同时从Session中删除。

2.HandlerMapping

HandlerMapping的继承结构如图:

image-20240321183031638

2.1AbstractHandlerMapping

是HandlerMapping的抽象实现,所有HandlerMapping都继承自AbstractHandlerMapping

AbstractHandlerMapping采用模板模式设计了HandlerMapping实现的整体结构,子类只需要通过模板方法提供一些初始值或具体的算法即可。

创建AbstractHandlerMapping之器

1.initApplicationContext方法

作用:初始化HandlerMapping的拦截器,包括其interceptors属性和adaptedInterceptors属性,具体初始化方式就是新增拦截器到属性中。

由于AbstractHandlerMapping继承了WebAppliactionObjectSupport,所以该对象创建时会调用父类的setApplicationContext方法,然后其方法又会调用本类的initApplicationContext()方法。即AbstractHandlerMapping的初始化就是在initApplicationContext方法实现

1
2
3
4
5
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}

2.extendInterceptors方法

作用:初始化HandlerMapping的interceptors属性

模板方法,用于给子类提供一个添加(或者修改)Interceptors的入口,不过在现有Spring MVC的实现中并没有使用。

3.detectMappedInterceptors方法

作用:将Spring MVC容器及父容器中的所有MappedInterceptor类型的Bean添加到HandlerMapping的 adaptedInterceptors 属性。即初始化HandlerMapping的adaptedInterceptors属性。

即将用户注册的按请求路径是否生效的拦截器MappedInterceptor添加到HandlerMapping的adaptedInterceptors属性中。

1
2
3
4
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}

4.initInterceptors方法

作用:初始化HandlerMapping的adaptedInterceptors属性。将interceptors属性中的拦截器全部适配到添加到adaptedInterceptors

1
2
3
4
5
6
7
8
9
10
11
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}

AbstractHandlerMapping之用

主要是getHandler方法在起作用。getHandler方法就是AbstractHandlerMapping的底层设计

getHandler方法的实现分为两部分,getHandlerExecutionChain之前是找Handler,getHandlerExecutionChain方法用于添加拦截器。

1.getHandler方法

找Handler的过程

  1. 通过 getHandlerInternal(request) 方法查找,这是模板方法,留给子类具体实现(也是子类主要做的事情)。
  2. 如果没有从子类获取到则使用默认的Handler。默认的Handler保存在AbstractHandlerMapping的一个Object类型的属性defaultHandler中。
  3. 如果默认Handler为null则方法返回null
  4. 如果找到的Handler类型是String,则从Spring MVC容器里查找相应名字的Bean

对应的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
。。。
}

2.getHandlerExecutionChain方法

作用:创建HandlerExecutionChain对象,然后将adaptedInterceptors属性中符合要求的拦截器添加进去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//1.创建 HandlerExecutionChain 对象
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//2.遍历adaptedInterceptors属性中的拦截器
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
//3.如果类型为 MappedInterceptor,则判断是否匹配当前请求,匹配则添加到 HandlerExecutionChain 的拦截器中
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(request)) { chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
//3.如果为其它类型拦截器,则直接添加到 HandlerExecutionChain 的拦截器中
chain.addInterceptor(interceptor);
}
}
//4.返回 HandlerExecutionChain 对象
return chain;
}

2.2AbstractUrlHandlerMapping系列

AbstractUrlHandlerMapping

它是通过请求的url来进行匹配查找处理器执行链的。

原理:将url与对应的Handler保存在一个Map中,在getHandlerInternal方法中使用url从Map中获取Handler,AbstractUrlHandlerMapping实现了getHandlerInternal方法具体从url中获取Handler的过程,而Map的初始化则交给了子类去完成

AbstractUrlHandlerMapping包含的属性如下:

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {

@Nullable
private Object rootHandler;

private boolean useTrailingSlashMatch = false;

private boolean lazyInitHandlers = false;

private final Map<String, Object> handlerMap = new LinkedHashMap<>();

private final Map<PathPattern, Object> pathPatternHandlerMap = new LinkedHashMap<>();

从源码来看,AbstractUrlHandlerMapping即支持直接literal字面量匹配也支持pattern模糊匹配。

PathPatternAntPathMatcher 都支持url的pattern模糊匹配,但PathPattern更高效且合适

1.getHandlerInternal方法

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
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//1.从请求中获取请求路径(不包括context path和请求参数),以供寻找handler处理器
String lookupPath = initLookupPath(request);
Object handler;
//2.根据Map查找handler
if (usesPathPatterns()) {
RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
//2.根据路径查找handler处理器
handler = lookupHandler(path, lookupPath, request);
}
else {
//2.根据路径查找handler处理器
handler = lookupHandler(lookupPath, request);
}
//3.如果未找到handler则尝试使用根路径的handler和默认handler
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if (StringUtils.matchesCharacter(lookupPath, '/')) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
//4.如果可以使用根handler或者默认handler则
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
//4.1校验handler是否适配请求
validateHandler(rawHandler, request);
//4.2构建最终的handler返回(设置处理器和拦截器)
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}

方法逻辑如下:

  • 调用父类initLookupPath方法。从请求对象获取解析(URL解码如:%20)后的请求路径,获取的请求路径不包括context path和请求参数
  • 调用本类lookupHandler方法。从handlerMap和pathPatternHandlerMap查找处理器
  • 如果从Map中未找到,则尝试使用根路径和默认的handler,获取到后要构建成处理器执行链。

2.initLookupPath方法

作用:获取真正用于匹配的请求路径,即不包含context path、不包含请求参数、且被URL解码的路径

1
2
3
4
5
6
7
8
9
10
11
12
protected String initLookupPath(HttpServletRequest request) {
//1.如果PathPatternParser属性在handlermapping存在,则通过ServletRequestPathUtils进行如下路径获取
if (usesPathPatterns()) { request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);
RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
String lookupPath = requestPath.pathWithinApplication().value();
return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);
}
else {
//2.如果不存在,则通过UrlPathHelper进行路径获取
return getUrlPathHelper().resolveAndCacheLookupPath(request);
}
}

3.lookupHandler方法(核心方法)

作用:根据指定的路径查找匹配的handler处理器执行链。

有两种模式:lookupHandler(path, lookupPath, request)和lookupHandler(lookupPath, request)

1
2
3
4
5
6
7
8
//AbstractUrlHandlerMapping.java的getHandlerInternal方法部分逻辑
if (usesPathPatterns()) {
RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
handler = lookupHandler(path, lookupPath, request);
}
else {
handler = lookupHandler(lookupPath, request);
}

lookupHandler方法用于使用lookupPath从Map中查找Handler,不过很多时候并不能直接通过字面量从Map中get到,因为很多Handler都是用了Pattern的匹配模式,如”/show/article/*”,这时就要进行模糊匹配。

此外一个lookupHandler还可能跟多个Pattern相匹配,这时还要选择其中最优的,所以查找过程并不是直接从Map中获取。

先分析简单的模式方法lookupHandler(lookupPath, request),此方法使用PathMatcher对lookupPath和pattern进行匹配

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
protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception {
//1.先直接从 handlerMap 中直接根据路径字面量获取handler,如果获取不到直接返回null,如果获取到直接返回
Object handler = getDirectMatch(lookupPath, request);
if (handler != null) {
return handler;
}

//============获取与lookupPath匹配的所有pattern===========

//初始化与lookupPath匹配的路径pattern字符串集合(mapkey集合)
List<String> matchingPatterns = new ArrayList<>();
//2.遍历 handlerMap 的key
for (String registeredPattern : this.handlerMap.keySet()) {
//2.1使用PathMatcher对象来匹配字符串pattern和lookupPath字符串路径
if (getPathMatcher().match(registeredPattern, lookupPath)) {
//2.2如果匹配则添加到 matchingPatterns 集合中
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
//2.2否则尝试给 registeredPattern尾部加/再进行匹配,如果匹配则添加到 matchingPatterns 集合中(这里原来的key被加了"/")
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", lookupPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}

//=======从matchingPatterns获取最匹配的pattern==========
//初始化一个最匹配的pattern字符串
String bestMatch = null;
//3.从 PathMatcher获取路径匹配比较器,对matchingPatterns匹配程度进行排序
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(lookupPath);
//4.如果 matchingPatterns 非空
if (!matchingPatterns.isEmpty()) {
//4.1使用匹配比较器进行比较排序 matchingPatterns
matchingPatterns.sort(patternComparator);
//4.2取排序最前面的,即匹配程度最高的(注意最高的可能有多个,这里暂时先不处理)
bestMatch = matchingPatterns.get(0);
}
//5.如果 bestMatch 非空
if (bestMatch != null) {
//5.1从 handlerMap 取出bestMatch对应的handler
handler = this.handlerMap.get(bestMatch);
//5.2如果获取不到,则可能是我们前面加了后缀导致
if (handler == null) {
//5.2.1去掉后缀获取handler
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
//5.2.2如果还获取不到,程序出错兜底报错
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
//5.3如果handler是字符串类型,从容器中获取对应的bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//5.4校验handler是否适配此请求
validateHandler(handler, request);
//5.5获取lookupPath与bestMatch动态匹配的部分路径
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, lookupPath);

//=============处理最匹配的pattern其实有多个的情况==========

// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
//6. 遍历matchingPatterns集合
for (String matchingPattern : matchingPatterns) {
//6.1使用匹配比较器,比较pattern的匹配程度是否与bestMatch相等
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
//使用 PathMatcher 获取pattern对应lookupPath的路径参数UriTemplateVariables的map。key为pattern的变量字符串,value为当前请求对应变量的值
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, lookupPath);
//url解码变量值,如果未进行解码的话
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
//7.封装handler为handler处理器执行链
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}

//8.如果未pattern模糊匹配成功,返回null
return null;
}

buildPathExposingHandler方法用于封装构造handler处理器执行链,用于给查找到的Handler注册两个拦截器PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor,这是两个内部拦截器,主要是将匹配的结果保存到请求对象的属性中

PathMatcher接口

作用:基于字符串的路径匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface PathMatcher {
//校验字符串是否为pattern
boolean isPattern(String path);
//校验指定path是否匹配指定pattern
boolean match(String pattern, String path);
//校验指定path是否匹配指定pattern的部分开头
boolean matchStart(String pattern, String path);
//提取指定path中与指定pattern动态匹配的部分
String extractPathWithinPattern(String pattern, String path);
//根据指定path和指定pattern提取路径变量Map对象
Map<String, String> extractUriTemplateVariables(String pattern, String path);
//获取指定path的Pattern比较器,用于比较Pattern对指定path的匹配程度
Comparator<String> getPatternComparator(String path);
//结合两个pattern字符串
String combine(String pattern1, String pattern2);
}

其默认实现类是AntPathMatcher。

AntPathMatcher

ant风格的路径匹配器

//TODO源码待解析

再**分析复杂的模式方法lookupHandler(path, lookupPath, request)**,此方法使用PathPattern进行匹配。

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
protected Object lookupHandler(
RequestPath path, String lookupPath, HttpServletRequest request) throws Exception {
//======1.通过字面量匹配直接从handlerMap获取handler==========

Object handler = getDirectMatch(lookupPath, request);
if (handler != null) {
return handler;
}

//======2.通过pattern匹配从pathPatternHandlerMap获取所有匹配的PathPattern====

//2.1初始化 matches 变量为null
List<PathPattern> matches = null;
//2.2遍历 pathPatternHandlerMap 集合的键
for (PathPattern pattern : this.pathPatternHandlerMap.keySet()) {
//2.3 使用PathPattern来匹配请求路径
if (pattern.matches(path.pathWithinApplication())) {
//匹配成功,则先视情况初始化 matches集合,再将pattern添加到集合
matches = (matches != null ? matches : new ArrayList<>());
matches.add(pattern);
}
}
//2.4如果没有匹配的PathPattern,则直接返回null
if (matches == null) {
return null;
}

//=====3.从matches匹配的中获取最匹配的PathPattern===========

//2.5如果有匹配的PathPattern
if (matches.size() > 1) {
//对 PathPattern 集合进行排序
matches.sort(PathPattern.SPECIFICITY_COMPARATOR);
}
//2.6获取首位作为最匹配的 PathPattern
PathPattern pattern = matches.get(0);
//2.7从pathPatternHandlerMap获取其对应的handler
handler = this.pathPatternHandlerMap.get(pattern);
//2.8如果 handler为 字符串类型,则从容器中获取handler
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//2.9校验handler是否与请求适配
validateHandler(handler, request);
//2.10获取请求路径与pattern动态匹配的部分
String pathWithinMapping = pattern.extractPathWithinPattern(path.pathWithinApplication()).value();
//2.11去掉“;”的路径参数
pathWithinMapping = UrlPathHelper.defaultInstance.removeSemicolonContent(pathWithinMapping);
//2.12封装构建handler处理器执行链
return buildPathExposingHandler(handler, pattern.getPatternString(), pathWithinMapping, null);
}

4.registerHandler方法

作用:初始化AbstractUrlHandlerMapping的Map类型属性,承担AbstractUrlHandlerMapping的创建工作。

由子类调用,这样不同的子类就可以通过注册不同的Handler将组件创建出来。

registerHandler(String[] urlPaths, String beanName)方法

1
2
3
4
5
6
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
//遍历传入的urlPaths,委托给registerHandler(urlPath, beanName)进行注册
for (String urlPath : urlPaths) {
registerHandler(urlPath, beanName);
}
}

registerHandler(String urlPath, Object handler)方法

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
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;

//1.如果handler是String类型,且没有设置lazyInitHandlers,则从容器中获取handler
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}

//2.从 handlerMap 直接获取对应urlPath的处理器
Object mappedHandler = this.handlerMap.get(urlPath);
//3.如果已存在对应urlPath的处理器
if (mappedHandler != null) {
//如果已存在的处理器和要注册的处理器不相同,则抛出异常
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException();
}
}
else {
//3.如果不存在对应urlPath的处理器
//4.判断urlPath是否为根路径
if (urlPath.equals("/")) {
//是根路径则,设置到rootHandler属性
setRootHandler(resolvedHandler);
}
//4.判断urlPath是否为/*路径
else if (urlPath.equals("/*")) {
//是/*路径,设置到默认 defaultHandler 属性
setDefaultHandler(resolvedHandler);
}
//4.如果是其它urlPath
else {
//4.1将 urlPath和对应的handler 放入handlerMap集合
this.handlerMap.put(urlPath, resolvedHandler);
//4.2如果使用PathPattern模式,还要放入 pathPatternHandlerMap集合
if (getPatternParser() != null) {
//这里会使用 PathPatternParser将urlPath解析为PathPattern对象!!!
this.pathPatternHandlerMap.put(getPatternParser().parse(urlPath), resolvedHandler);
}
}
}
}

SimpleUrlHandlerMapping

是AbstractUrlHandlerMapping的直接实现子类。

通过重写父类的initApplicationContext方法在创建组件对象时调用**registerHandlers(Map<String, Object> urlMap)**方法来完成Handler的注册,方法内部又调用了registerHandler(url, handler)父类方法。

原理:通过registerHandlers将urlMap读取出来,然后将key带上/前缀,将value去掉空白符号,然后注册。

SimpleUrlHandlerMapping类非常简单,就是直接将配置的内容注册到了AbstractUrlHandlerMapping的属性中。

AbstractDetectingUrlHandlerMapping

是AbstractUrlHandlerMapping的子抽象类。

也是通过重写父类的initApplicationContext方法来完成Handler的注册,里面调用了detectHandlers方法。

原理:从容器中获取所有的beanName,遍历beanName尝试解析其对应的urls,解析出来非空则执行注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected void detectHandlers() throws BeansException {
//1.获取 ApplicationContext
ApplicationContext applicationContext = obtainApplicationContext();
//2.从 ApplicationContext 获取所有的beanName
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));

//3.遍历所有的beanName
for (String beanName : beanNames) {
//3.1尝试从beanName解析出 urls
String[] urls = determineUrlsForHandler(beanName);
//3.2如果解析出的urls非空,调用父类方法执行注册
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
}
}

BeanNameUrlHandlerMapping实现类实现非常简单,检查beanName和alias是不是以”/“开头,如果是则将beanName和alias做为urls

2.3AbstractHandlerMethodMapping系列

这个系列是将Method作为Handler来使用的,这也是我们现在用的最多的一种Handler,经常使用的@RequestMapping所注释的方法就是这种Handler,它有一个专门的类型HandlerMethod

它是Request和HandlerMethod的映射Map。

创建AbstractHandlerMethodMapping系列之器

泛型T是用来代表匹配Handler的条件专门使用的一种类,这里的条件不仅是url还可以有很多其他条件,如请求类型、请求参数、Header等。

RequestCondition

默认泛型使用的是RequestMappingInfo。RequestMappingInfo实现了RequestCondition接口,此接口专门用于保存从request提取出的用于匹配Handler的条件

1
2
3
4
5
6
7
public interface RequestCondition<T> {
//结合请求条件
T combine(T other);
//校验请求是否匹配当前条件,然后返回匹配的条件或者null
T getMatchingCondition(HttpServletRequest request);
//在当前请求下,比较当前请求条件和other请求条件
int compareTo(T other, HttpServletRequest request);

image-20240325141917597

AbstractRequestCondition中重写了equals、hashCode和toString三个方法,有8个子类,除了CompositeRequestCondition外每一个子类表示一种匹配条件。

RequestCondition的另一个实现就是这里要用的RequestMappingInfo,它里面其实就是用七个属性保存了七个RequestCondition,在匹配时使用那七个变量进行匹配,这也就是可以在@RequestMapping中给处理器指定多种匹配方式的原因。

AbstractHandlerMethodMapping中的核心属性是private final MappingRegistry mappingRegistry = new MappingRegistry();即一个映射注册器对象。这个注册器维护了所有的关于HandlerMethod的映射。

MappingRegistry又封装了三个最重要的Map:

1
2
3
4
5
6
7
class MappingRegistry {
//1.匹配条件RequestCondition与HandlerMethod的对应关系,当然这里将HandlerMethod封装成MappingRegistration
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
//2.uri与匹配条件的对应关系
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
//3.name与HandlerMethod集合的对应关系
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

这里的name是使用HandlerMethodMappingNamingStrategy策略的实现类从HandlerMethod中解析出来的,默认使用RequestMappingInfoHandlerMethodMappingNamingStrategy实现类,解析规则是:类名里的大写字母组合+”#”+方法名

正常匹配过程中无需使用这个Map。

1.afterPropertiesSet方法

作用:初始化AbstractHandlerMethodMapping的属性

AbstractHandlerMethodMapping实现了InitializingBean接口,所以spring容器会自动调用其afterPropertiesSet方法,afterProperties方法又交给initHandlerMethods方法完成具体的初始化。

1
2
3
public void afterPropertiesSet() {
initHandlerMethods();
}

2.initHandlerMethods方法

作用:从容器中扫描所有的bean,然后根据一定的规则筛选出Handler,然后注册。默认会去除以”scopedTarget.”开头的beanName。

1
2
3
4
5
6
7
8
9
10
11
12
protected void initHandlerMethods() {
//1.获取容器中所有的beanName并进行遍历
for (String beanName : getCandidateBeanNames()) {
//1.2如果beanName不是以"scopedTarget."开头
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//进行筛选bean,通知注册HandlerMethod
processCandidateBean(beanName);
}
}
//2.注册HandlerMethod 之后的后处理,这里默认是日志记录
handlerMethodsInitialized(getHandlerMethods());
}

3.processCandidateBean方法

作用:校验指定beanName的类型是否为handler,如果是则调用detectHandlerMethods(beanName)方法进行注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void processCandidateBean(String beanName) {
//1.获取beanName对应的beanType
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {

}
//2.调用isHandler方法校验beanType,校验成功进行注册
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}

isHandler方法由子类实现,即如何筛选容器中的bean由子类实现。

4.detectHandlerMethods方法(核心方法

作用:注册。

通过一系列反射操作,先获取handler的真实类型,再从真实类型中遍历其方法反射对象,并通过getMappingForMethod(method, userType)方法获取请求条件对象,并构造出methods映射集合,最后遍历methods来调用registerHandlerMethod(handler, invocableMethod, mapping)进行注册到map中。

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
protected void detectHandlerMethods(Object handler) {
//1.获取handler的类型
Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());

if (handlerType != null) {
//2.获取真实的类型,解析动态代理的真实类型
Class<?> userType = ClassUtils.getUserClass(handlerType);
//3. 使用MethodIntrospector遍历真实类型的中的方法,如果方法是HandlerMethod则添加Method和条件对象的映射到methods集合中
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException();
}
});

//4.遍历methods集合
methods.forEach((method, mapping) -> {
//4.1使用AopUtils获取方法反射对象的真正代理方法反射对象
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//4.2调用registerHandlerMethod将方法和请求条件注册到map中
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}

方法分为两步:

  1. 首先从传入的处理器中找到符合要求的方法,根据getMappingForMethod方法来找。
  2. 然后使用registerHandlerMethod进行注册(也就是保存到Map中)。

5.RequestMappingHandlerMapping的getMappingForMethods方法

作用:通过类上和方法上@RequestMapping注解来创建RequestMappingInfo对象,并把它们合并返回。

它是根据@RequstMapping注解来找匹配条件的,如果没有则返回null,如果有则根据注解的内容来创建RequestMappingInfo类型的匹配条件对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//RequestMappingHandlerMapping.java
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//1.根据【方法反射对象】创建 RequestMappingInfo
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//2.根据【类class对象】创建 RequestMappingInfo
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
//3.如果类级别的注解非空,则组合匹配条件
if (typeInfo != null) {
info = typeInfo.combine(info);
}
//4.从handlermapping对象属性中获取路径前缀
String prefix = getPathPrefix(handlerType);
//5.如果路径前缀非空,则继续组合匹配条件
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
//6.返回组合好的匹配条件,或者null
return info;
}

6.RequestMappingHandlerMapping的createRequestMappingInfo方法

作用:通过方法Mthod反射对象或者所属类Class对象上的注解对象来创建RequestMappingInfo

1
2
3
4
5
6
7
8
9
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
//1.获取元素(方法或类反射对象)上的注解对象
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
//2.根据元素对象获取对应的自定义的条件对象,这里默认为null
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
//3.如果注解非空,则根据注解对象真正执行创建 RequestMappingInfo对象
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

7.registerHandlerMethod方法

作用:注册HandlerMethod相关映射到mappingRegistry属性中。

1
2
3
4
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
//调用 mappingRegistry 属性对象的register方法
this.mappingRegistry.register(mapping, handler, method);
}

8.MappingRegistry类的register方法

作用:将请求条件对象、handler的名字、Mthod反射对象转化一下注册到规范的三个Map中。

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
public void register(T mapping, Object handler, Method method) {
//1.获取写锁 (此时不允许获取读锁和写锁)
this.readWriteLock.writeLock().lock();
try {
//2.调用 createHandlerMethod方法来创建HandlerMethod
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
//3.校验要注册的HanderMethod是否已经注册,如果注册则判断是否矛盾
validateMethodMapping(handlerMethod, mapping);
//4.获取请求条件对象的uri
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
//5.以uri为key,请求条件对象为value添加到 pathLookup 映射【pathLookup初始化】
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
//6.从handerMethod和mapping中获取name
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
//7.以name为key,handlerMethod为value添加到 nameLookup 映射【nameLookup初始化】
addMappingName(name, handlerMethod);
}

//8. corsLookup初始化
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}

//9.以请求条件为key,以以上构建结果封装起来的MappingRegistration为value添加到 registry 映射【registry初始化】
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
finally {
//10.释放写锁
this.readWriteLock.writeLock().unlock();
}
}

首先检查一下registry这个Map里是不是已经有这个匹配条件了,如果有而且所对应的值和现在传入的HandlerMethod不是同一个则抛出异常;否则依次添加到三个Map里

AbstractHandlerMethodMapping系列之用

1.getHandlerInternal方法

作用:根据请求对象获取HandlerMethod对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//1.从请求对象提取 lookupPath
String lookupPath = initLookupPath(request);
//2.获取读锁
this.mappingRegistry.acquireReadLock();
try {
//3.根据请求路径查询对应的 HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//4.返回
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//5.释放读锁
this.mappingRegistry.releaseReadLock();
}
}

2.lookupHandlerMethod方法

作用:通过lookupPath和请求对象,从mappingRegistry查找对应最匹配的的HandlerMethod返回。

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
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
//===========获取匹配项matches================

//可能匹配的集合。内部类封装了 MappingRegistry的registry属性的键和值,即封装了请求条件和HandlerMethod
List<Match> matches = new ArrayList<>();
//1.根据lookupPath从 mappingRegistry的pathLookup属性中查找对应的多个请求条件对象集合
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
//2.如果非空,则遍历所有请求条件创建对应请求的请求条件对象,并创建对应Match将其添加到 matches集合。注意这里的请求条件已经根据请求对象发生了改变。
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
//3.如果matches集合为空,尝试将所有请求条件和handlerMethod的映射添加到matches,会通过请求条件对象的getMatchingCondition方法来校验当前请求是否匹配条件。
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}

//===============获取最匹配项bestMatch===========

//4. 如果matches集合非空,即有符合的请求条件对象
if (!matches.isEmpty()) {
//5.对Match进行排序。底层是使用RequstCondition的compareTo方法实现的排序
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
//6.取排序首位
bestMatch = matches.get(0);
//7.如果是option类型请求返回一个默认的handlerMethod
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
}
else {
//7.如果是其它类型请求,获取第二匹配的匹配条件
Match secondBestMatch = matches.get(1);
//如果匹配程度相等,则抛出异常
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
//8.设置请求属性,将请求的handlerMethod设置进去
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
//9. 匹配成功后的一些处理
handleMatch(bestMatch.mapping, lookupPath, request);
//10.返回最匹配的对应的 HandlerMethod
return bestMatch.getHandlerMethod();
}
else {

//==================匹配失败===============

//4.匹配项集合为空,调用 handleNoMatch方法,匹配失败后的一些处理
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}

2.4小结

HandlerMapping的整体结构在AbstractHandlerMapping中设计,简单来说其功能就是根据request找到Handler和Interceptors,组合成HandlerExecutionChain类型并返回。找到Handler的过程通过模板方法getHandlerInternal留给子类实现,查找Interceptors则是AbstractHandlerMapping自己完成的

//TODO RequestMappingHandlerMapping.java源码待解析

3.HandlerAdapter

HandlerAdapter是具体使用Handler来干活的,每个HandlerAdapter封装了一种Handler的具体使用方法

image-20240325220431900

RequestMappingHandlerAdapter的实现非常复杂,而其它的非常简单,因为其它三个Handler的格式都是固定的,只需要调用固定的方法即可,但是RequestMappingHandlerAdapter所处理的Handler可以是任意的方法,没有任何约束,这就极大地增加了难度。

3.1RequestMappingHandlerAdapter概述

AbstractHandlerMethodAdapter非常简单,三个接口方法分别调用了自定义的模板方法supportsInternal、handleInternal、getLastModifiedInternal。即它只支持handler类型为HandlerMethod类型

RequestMappingHandlerAdapter是Spring MVC最复杂的组件!!!

核心方法就是handleInternal方法。这个方法是实际使用Handler处理请求的方法。具体过程大致分为三步:

  1. 准备好处理器所需要的参数
  2. 使用处理器处理请求
  3. 处理返回值,也就是将不同类型的返回值统一处理成ModelAndView类型。

这三步里面第2步是最简单的,直接使用反射技术调用处理器执行就可以了,第三步也还算简单,最麻烦的是第一步

这第一步根据处理器的需要设置参数,而参数类型、个数都是不确定的,所以难度非常大。

参数具体解析是使用HandlerMethodArgumentResolver类型的组件完成的,不同类型的参数使用不同的ArgumentResolver来解析。

@InitBinder注解

只用于注解在方法上,有@InitBinder注解的方法用于初始化WebDataBinder,我们可以在其中做一些WebDataBinder初始化的工作,如注册校验器、注册自己的参数编辑器等。

可以在Controller中通过以下代码注册一个转换Date类型的编辑器,这样就可以将”yyyy-MM-dd”类型的String转换成Date类型。

image-20240326212215593

@ModelAttribute注解

@ModelAttribute注解如果用在方法上,则用于设置参数,它会在执行处理(执行HandlerMethod前)前将参数设置到Model中。

@ModelAttribute注解如果用在参数上,则表示需要使用指定的ArgumentResolver来解析参数。

如果想让以上两个注解在所有处理器中都起作用,我们可以定义一个类,然后在类上加@ControllerAdvice注解,并将@InitBinder、@ModelAttribute注释的方法放进去就可以了,这样每个Handler调用前都会调用这些方法。

3.2RequestMappingHandlerAdapter自身结构

RequestMappingHandlerAdapter自身的结构并不复杂,不过其中使用了很多组件。所以要准确理解各个组件的作用。

创建RequestMappingHandlerAdapter之器

RequestMappingHandlerAdapter的创建在afterPropertiesSet方法中实现。

1.afterPropertiesSet方法

作用:初始化 RequestMappingHandlerAdapter 的6个属性组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void afterPropertiesSet() {
//1.通过@ContorllerAdvice注解的类初始化属性modelAttributeAdviceCache、initBinderAdviceCache、requestResponseBodyAdvice
initControllerAdviceCache();

//2.初始化属性 argumentResolvers。用于处理参数
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//3.初始化属性 initBinderArgumentResolvers。
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//4.初始化属性 returnValueHandlers。
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}

介绍一下这六个属性:

  • argumentResolvers:用于给处理器方法注解了@ModelAttribute的方法设置参数。
  • initBinderArgumentResolvers:用于给注解了@InitBinder的方法设置参数。
  • returnValueHandlers:用于将处理器的返回值处理成ModelAndVieW的类型。
  • modelAttributeAdviceCache和initBinderAdviceCache:分别用于缓存@ControllerAdvice注解里面注释了@ModelAttribute和@InitBinder的方法,也就是全局的@ModelAttribute和@InitBinder的方法。而每个处理器自己的@ModelAttribute和@InitBinder的方法是在第一次使用处理器处理请求时缓存起来的。
  • requestResponseBodyAdvice:用来保存实现了RequestBodyAdvice或者ResponseBodyAdvice接口的类。

这些属性都是复数形式,也就是可以有多个,在使用的时候是按顺序调用的,所以这些属性初始化时的添加顺序就非常重要了。

2.initControllerAdviceCache方法

作用:从容器中获取bean来初始化modelAttributeAdviceCache、initBinderAdviceCache和requestResponseBodyAdvice属性

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 void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
//1.从容器中获取所有注释了 @ControllerAdvice的bean,并封装成ControllerAdviceBean集合
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

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

//2.遍历 adviceBeans 集合
for (ControllerAdviceBean adviceBean : adviceBeans) {
//2.1获取 adviceBean 的类型 beanType
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}

//2.2获取 beanType 中所有的 注解了@ModelAttribute的方法 attrMethods
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
//2.3向 【modelAttributeAdviceCache 属性】中添加 adviceBean 对 attrMethods 的映射
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}

//2.4获取 beanType 中所有的 注解了@InitBinder的方法 binderMethods
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
//2.5向 【initBinderAdviceCache 属性】中添加 adviceBean 对 binderMethods 的映射
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}

//2.6如果beanType是 RequestBodyAdvice 或者 ResponseBodyAdvice,则向 requestResponseBodyAdviceBeans集合 中添加adviceBean
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}

//3.如果 requestResponseBodyAdviceBeans 集合非空,则向 【requestResponseBodyAdvice 属性】中添加 requestResponseBodyAdviceBeans
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
}

3.getDefualtXXX方法

作用:通过new直接创建组件初始化argumentResolvers、initBinderArgumentResolvers、returnValueHandlers属性。

参数解析器分为四类:通过注解解析的解析器、通过类型解析的解析器、自定义的解析器和可以解析所有类型的解析器。

通过定义内置的参数解析器,也相当于定义了方法参数的来源

RequestMappingHandlerAdapter之用

RequestMappingHandlerAdapter处理请求的入口方法是handleInternal

1.handleInternal方法

作用:校验请求、执行请求、添加Cache-Control响应头

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
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ModelAndView mav;
//1.根据请求对象的类型和session校验请求 (默认情况下校验跳过)
checkRequest(request);

// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
//2.获取请求session对象
HttpSession session = request.getSession(false);
//3.如果session非空
if (session != null) {
//3.1获取session对应的锁对象
Object mutex = WebUtils.getSessionMutex(session);
//3.2线程获取锁对象,然后调用 invokeHandlerMethod 方法
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
//3.如果session对象为空,则直接调用 invokeHandlerMethod 方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
//2.直接调用 invokeHandlerMethod 方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}

//4.如果响应头还没有设置Cache-Control,则处理设置相应头 (默认会进入代码块,这里设置Cache-Control响应头控制浏览器响应缓存)【实现逻辑:如果有@SessionAttributes注解则阻止使用缓存,否则什么也不做】
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
//second = 0,阻止浏览器使用缓存
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
//默认执行 second = -1 ,即什么也不执行
prepareResponse(response);
}
}

//5.返回modelandview
return mav;
}

这里真正起作用的代码只有两句,也是两个方法:checkRequest和invokeHandlerMethod方法。这个方法通过synchronizeOnSession属性设置是否通过同步session来执行方法。

如果handlerMethod方法参数中带有注解@SessionAttributes,就会执行applyCacheSeconds(response,0(默认的))方法,如果没有就会执行prepareResponse(response) -> applyCacheSeconds(response,-1(默认的))方法。

applyCacheSeconds会调用applyCacheControl,并根据second不同设置不同的Cache-Control响应头,以控制浏览器对响应的缓存策略。

RequestMappingHandlerAdapter的cacheSecondsForSessionAttributeHandlers和cacheSeconds属性其实与服务端的Session超时并没有关系,而是用于设置客户端浏览器response缓存相关的Header参数。

@SessionAttributes注解

  • 设置:在controller类上注解@SessionAttributes(),设置模型Model中要保存到SessionAttribute的属性名和类型,然后在处理器中将参数设置到model中。

  • 使用:后续请求直接从Model获取或者@ModelAttribute的参数获取。

2.invokeHandleMethod方法

这个方法非常重要,它具体执行请求的处理。

invokeHandlerMethod方法首先使用request和response创建了ServletWebRequest类,在ArgumentResolver解析参数时使用的request就是这个webRequest

接着对WebDataBinderFactory、ModelFactory、ServletInvocableHandlerMethod这三个类型的变量进行了定义和初始化。

WebDataBinderFactory

作用:是用来创建 WebDataBinder 的,WebDataBinder用于参数绑定,主要功能就是实现参数跟String之间的类型转换。ArgumentResolver在进行参数解析的过程中会用到WebDataBinder,另外ModelFactory在更新Model时也会用到它。

WebDataBinder

继承自DataBinder类,有三个直接实现类。

image-20240330210602752

先来看一下DataBinder。它实现了两个接口TypeConverter、PropertyEditorRegistry,即DataBinder和WebDataBinder是一个类型转换器和属性编辑注册器。通过属性编辑注册器接口来注册属性编辑器到DataBinder,再通过属性编辑器实现类型转换器(java内置提供了一些属性编辑器来实现string=>目标类型的转换)。

即DataBinder核心是类型转换器,而类型转换器的核心是属性编辑器。类型转换器提供了方便的高层封装方法。

image-20240330214201233

类型转换器接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface TypeConverter {
//1.将value转换为指定requiredType类型
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;

//2.将value转换为指定类型requiredType类型,并提供了参数反射对象的封装MethodParameter对象
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam) throws TypeMismatchException;

//3.将value转换为指定类型requiredType类型,并提供了对象的Field属性反射对象
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
throws TypeMismatchException;

//4.将value转换为指定类型requiredType类型,并提供了类型描述对象TypeDescriptor
default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
}

属性编辑器注册器接口

1
2
3
4
5
6
7
8
9
public interface PropertyEditorRegistry {
//1.注册针对requiredType类型的属性编辑器
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);

//2.注册针对requiredType类型和指定属性路径(属性名/嵌套属性名路径)的属性编辑器
void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);

//3.查找指定类型和指定路径的属性编辑器
PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);

DataBinder的作用如下:Binder that allows for setting property values on a target object, including support for validation and binding result analysis.可以设置对象属性,并进行校验和绑定结果分析

WebDataBinderFactory的创建过程就是将符合条件的注释了@InitBinder的方法找出来,并使用它们创建出ServletRequestDataBinderFactory类型的WebDataBinderFactory。

1.getDataBinderFactory方法

作用:使用HandlerMethod创建WebDataBinderFactory

WebDataBinderFactory的创建过程就是将符合条件的注释了@InitBinder的方法找出来,并使用它们创建出ServletRequestDataBinderFactory类型的WebDataBinderFactory

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
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
//1.通过 HandlerMethod 获取方法所在的bean类型,即处理器类型
Class<?> handlerType = handlerMethod.getBeanType();

//=====查找处理器级别的@InitBinder注解的方法============

//2.以处理器类型为key在 initBinderCache 查找方法反射对象集合
Set<Method> methods = this.initBinderCache.get(handlerType);
//3.如果为空,则进行初始化(方法调用时初始化处理器中的@InitBinder注解的方法,懒加载机制)
if (methods == null) {
//使用 MethodIntrospector.selectMethods来查找处理器类型进行初始化
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}

//4.创建一个 【initBinderMethods 集合】。存储元素类型为InvocableHandlerMethod
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();

//=====查找全局级别的@InitBinder注解的方法===========

//5.从 initBinderAdviceCache 获取@InitBinder注解的方法,并封装到 initBinderMethods 集合
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
//5.1如果 controllerAdviceBean 支持该 handlerType
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
//5.2从 controllerAdviceBean 解析出对应的bean对象
Object bean = controllerAdviceBean.resolveBean();
//5.3遍历反射方法,封装添加到 initBinderMethods【全局方法添加】
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});

//======封装处理器级别的@InitBinder注解的方法===========

//6.遍历处理器级别的 @InitBinder注解的方法反射对象
for (Method method : methods) {
//6.1获取方法所属bean对象/名字
Object bean = handlerMethod.getBean();
//6.2封装添加到 initBinderMethods【处理器级别方法添加】
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
}

//传入方法所属bean和方法反射对象,创建InvocableHandlerMethod
//InvocableHandlerMethod 是 HandlerMethod 的子类
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
//1.直接new创建InvocableHandlerMethod
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
//2.设置binderMethod initBinderArgumentResolvers参数解析器,用于解析@InitBinder注解的方法参数
if (this.initBinderArgumentResolvers != null) {
binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
}
//3.设置binderMethod DataBinderFactory,用于参数绑定
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
//4.设置binderMethod 参数名发现器
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
return binderMethod;
}

//最终创建的是 ServletRequestDataBinderFactory 类型的 InitBinderDataBinderFactory
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
throws Exception {
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}

ModelFactory

ModelFactory 是用来处理Model的,主要包含两个功能:1.在处理器具体处理之前对Model进行初始化,2.在处理完请求后对Model参数进行更新

对Model初始化具体包括三部分内容:1.将原来的SessionAttributes中的值设置到Model;2.执行相应注释了@ModelAttribute的方法并将其设置到Model;3.处理器中注释了@ModelAttribute的参数如果同时在SessionAttributes也配置了,而且在mavContainer中还没有值则从全部SessionAttributes(可能是其它处理器设置的值)中查找出并设置进去。

2.getModelFactory方法

作用:使用HandlerMethod和WebDataBinderFactory创建ModelFactory

ModelFactory的创建过程就是将注释了@ModelAttribute却没有注释@RequestMapping的方法找出来并使用它们创建出ModelFactory对象

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
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
//1.从sessionAttributesHandlerCache根据 handlerMethod获取SessionAttributesHandler对象
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);

//=========查找处理器级别的@ModelAttribute方法===========

//2.从 handlerMethod获取处理器类型
Class<?> handlerType = handlerMethod.getBeanType();
//3.从 modelAttributeCache 根据handlerType获取@ModelAttribute的反射方法
Set<Method> methods = this.modelAttributeCache.get(handlerType);
//4.若为空,则进行初始化(方法调用时初始化处理器中的@ModelAttribute注解的方法,懒加载机制)
if (methods == null) {
//从方法反射对象中获取没有@RequestMapping注解但有@ModelAttribute注解的方法
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
//将处理器类型和反射方法映射放入 modelAttributeCache缓存
this.modelAttributeCache.put(handlerType, methods);
}

//5.创建 【InvocableHandlerMethod 的集合】
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();

//============处理器全局级别@ModelAttribute方法=========

//6.遍历 modelAttributeAdviceCache

this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
//6.1如果controllerAdviceBean支持handlerType
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
//6.2获取 controllerAdviceBean 的bean对象
Object bean = controllerAdviceBean.resolveBean();
//6.3遍历对应的 methodSet
for (Method method : methodSet) {
//6.4调用createModelAttributeMethod方法创建InvocableHandlerMethod并添加到 attrMethods集合。
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});

//============处理处理器级别@ModelAttribute方法===========
//7.遍历处理器级别的 @ModelAttribute反射方法
for (Method method : methods) {
//7.1获取处理器的bean对象
Object bean = handlerMethod.getBean();
//7.2调用createModelAttributeMethod方法创建InvocableHandlerMethod并添加到 attrMethods集合。
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
//8.创建 ModelFactory 对象,封装attrMethods、binderFactory、sessionAttrHandler
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

//获取HandlerMethod对应的SessionAttributesHandler
private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {
//依据HandlerMethod的beanType从sessionAttributesHandlerCache获取,如果获取不到,则new 创建指定type的SessionAttributesHandler
return this.sessionAttributesHandlerCache.computeIfAbsent(
handlerMethod.getBeanType(),
type -> new SessionAttributesHandler(type, this.sessionAttributeStore));
}

ServletInvocableHandlerMethod

非常重要,它继承自HandlerMethod,并且可以直接执行。实际请求的处理就是通过它来执行的,参数绑定、处理请求以及返回值处理都在它里边完成

3.createInvocableHandlerMethod方法

作用:封装HandlerMethod为ServletInvocableHandlerMethod对象,提供执行方法、绑定参数、处理返回值的场所。

1
2
3
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethod(handlerMethod);
}

在创建三个变量(binderFactory、modelFactory、invocableMethod)后,还有三步(这里省略了异步处理):
1.新建传递参数的ModelAndViewContainer容器,并将相应参数设置到Model中,
2.执行请求,
3.请求处理完后进行一些后置处理。

4.invokeHandlerMethod方法

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
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

//1.使用 request、response封装成ServletWebRequest
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//2.使用 handlerMethod 创建 binderFactory 对象(处理@InitBinder注解)
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//3.使用 handlerMethod、binderFactory创建 ModelFactory(处理@ModelAttribute注解)
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//4.使用 handlerMethod 创建 invocableMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//4.1设置 invocableMethod 的方法参数解析器,将adapter的参数解析器传递给invocableMethod
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
//4.2设置 invocableMethod 的方法返回值处理器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
//4.3设置 invocableMethod 的参数绑定工厂
invocableMethod.setDataBinderFactory(binderFactory);
//4.4设置 invocableMethod 的参数名发现器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

//5.创建 ModelAndViewContainer 对象
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//6.将request请求中inputfalshmap的重定向传递属性添加到mavContainer
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//7. 使用modelFactory初始化mavContainer中的model
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
//8.设置mavContainer属性ignoreDefaultModelOnRedirect
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//9.异步处理
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}

//10.使用 invocableMethod 调用invokeAndHandle方法真正调用处理器方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//11.方法调用后处理,返回ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

5.getModelAndView方法

作用:使用ModelAndViewContainer、ModelFactory、NativeWebRequest创建ModelAndView对象,对请求调用进行后处理

1.调用ModelFactory的updateModel方法更新Model
2.根据mavContainer创建MdoelAndView
3.如果mavContainer里的model是RedirectAttributes类型,将其设置到FlashMap。

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
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//1.使用modelFactory来更新mavContainer中的Model(包括更新Model对应的SessionAttributes和Model设置BindingResult)
modelFactory.updateModel(webRequest, mavContainer);
//2.使用mavContainer来判断请求是否已经被处理,已被处理则直接返回null
if (mavContainer.isRequestHandled()) {
return null;
}
//3.从 mavContainer 获取model
ModelMap model = mavContainer.getModel();
//4.依据mavContainer的视图名称、model模型内容、状态创建ModelAndView对象
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
//5.如果 mavContainer的view属性不是字符串,则直接设置到mav的view属性中
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
//6.如果 mavContainer 中的model是RedirectAttributes类型,则获取model中的flashAttributes,并设置到请求属性中。(只有返回redirct视图时此块代码才会调用)
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
//7.返回创建的mav
return mav;
}

这里的model只有处理器在返回redirect类型的视图才可能是RedirectAttributes类型,否则不会是RedirectAttributes类型,也就是说在不返回redirect类型视图的处理器中即使使用RedirectAttributes设置了变量也不会保存到FlashMap中。

3.3ModelAndViewContainer

在 RequestMappingHandlerAdapter 的 invokeHandlerMethod方法 中创建并使用。

ModelAndViewContainer承担着整个请求过程中数据的传递工作。

  • 在处理器中使用了 Model 或者 ModelMap 时ArgumentResolver会传入defaultModel,它是BindingAwareModelMap类型,既继承了ModelMap又实现了Model接口,所以Model或者ModelMap其实使用的是同一个对象。
  • 处理器中RedirectAttributes类型的参数ArgumentResolver会传入redirectModel,它实际上是RedirectAttributesModelMap类型。

getModel方法返回redirectModel的情况下,在处理器中设置到Model的参数就不会被mav使用了(设置SessionAttributes除外)。

getModel方法返回defaultModel,设置到RedirectAttributes中的参数也将丢弃,也就是说在返回的view不是redirect类型时,即使处理器使用RedirectAttributes参数设置了值也不会传递到下一个请求。

只有将参数设置到Model或者ModelMap里才能使用SessionAttributes缓存,设置到RedirectAttributes里的参数不行。

3.4SessionAttributesHandler和SessionAttributeStore

在 RequestMappingHandlerAdapter 的 getModelFactory方法中创建并缓存到其属性sessionAttributesHandlerCache中,每个controller对应一个自己的SessionAttributesHandler。且每个SessionAttributesHandler都封装了其属性sessionAttributeStore。

SessionAttributesHandler用来处理@SessionAttributes注解的参数,来创建SessionAttributesHandler对象。

SessionAttributesHandler的具体存储工作是交给SessionAttributeStore去做的,而且使用的统一为 RequestMappingHandlerAdapter 的SessionAttributeStore。SessionAttributeStore并不是保存数据的容器,而是保存数据的工具,具体保存数据的容器使用的是Session。

SessionAttributeHandler是在ModelFactory中使用的。

3.5ModelFactory

是用来维护Model的,具体包含两个功能:1.初始化Model,2.处理器执行后将Model中相应的参数更新到SessionAttributes中。

初始化Model

主要是在处理器执行前将相应数据设置到Model中,是通过调用initModel方法完成的。

initModel方法

1.从sessionAttributes中取出保存的参数,并合并到mavContainer的Model中
2.执行注解了@ModelAttribute的方法并将结果设置到mavContainer的Model中
3.判断既注解了@ModelAttribute又在@SessionAttributes注解中(参数名或者参数类型在注解中设置了)的参数是否已经设置到mavContainer的Model中,如果没有则使用sessionAttributesHandler从sessionAttributeStore中获取并设置到mavContainer中。

第三步跟第一步的区别是第一步是将当前处理器保存的所有SessionAttributes属性合并到了mavContainer,而第三步可以使用其它处理器中保存的SessionAttributes属性来设置注解了@ModelAttribute的参数。

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
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {

//1.从sessionAttributes中获取已经保存的参数,并保存到mavContainer中
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
container.mergeAttributes(sessionAttributes);

//2.执行注解了@ModelAttribute的方法并将结果设置到mavContainer的Model中
invokeModelAttributeMethods(request, container);

//3.遍历既注解了@ModelAttribute又在@SessionAttributes注解中出现的方法参数
for (String name : findSessionAttributeArguments(handlerMethod)) {
//如果当前Model中没有此参数
if (!container.containsAttribute(name)) {
//3.1从sessionAttributes获取参数值
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
//3.2如果获取不到,则抛出异常
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
//3.2获取到则添加到Model中
container.addAttribute(name, value);
}
}
}

invokeModelAttributeMethods方法

作用:遍历的方式执行注解了@ModelAttribute的方法并将结果设置到mavContainer的Model中

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
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
throws Exception {

//如果 modelMethods 属性非空
while (!this.modelMethods.isEmpty()) {
//1.获取下一个注释了@ModelAttribute注解的方法
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
//2.获取方法上的@ModelAttribute注解对象
ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
//3.如果@ModelAttribute注解设置了name且mavContainer中存在此参数则直接跳过后续处理
if (container.containsAttribute(ann.name())) {
if (!ann.binding()) {
container.setBindingDisabled(ann.name());
}
continue;
}

//4.【执行@ModelAttribute注解的方法】
Object returnValue = modelMethod.invokeForRequest(request, container);

//5.如果@ModelAttribute注解的方法返回类型为void,跳过后续处理(因为在执行方法时就设置好了Model)
if (modelMethod.isVoid()) {
//如果 @ModelAttribute注解设置了value值,此时无效,因为没有返回值用于设置值
if (StringUtils.hasText(ann.value())) {
if (logger.isDebugEnabled()) {
logger.debug("Name in @ModelAttribute is ignored because method returns void: " +
modelMethod.getShortLogMessage());
}
}
//跳过后续处理
continue;
}

//6.调用 getNameForReturnValue方法获取参数名
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
//获取@ModelAttribute注解的binding设置,来设置是否允许绑定此参数
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}
//7.如果mavContainer中没有此属性,则添加到mavContainer中
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}

getNameForReturnValue方法

作用:根据方法返回值和返回类型/@ModelAttribute注解获取Model的参数名,此方法主要实现基于@ModelAttribute注解获取Model的参数名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static String getNameForReturnValue(@Nullable Object returnValue, MethodParameter returnType) {
//1.根据返回类型对象获取方法注解@ModelAttribute
ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
//2.如果注解非空且注解中设置了 value,则直接返回value值
if (ann != null && StringUtils.hasText(ann.value())) {
return ann.value();
}
else {
//2.如果注解中没有设置 value,则根据方法类型获取参数名
//获取方法反射对象
Method method = returnType.getMethod();
//获取此方法所在的类class对象
Class<?> containingClass = returnType.getContainingClass();
//解析方法真实的返回类型class对象
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
//调用Conventions.getVariableNameForReturnType方法根据方法返回类型获取参数名
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
}
}

Conventions.getVariableNameForReturnType方法

作用:根据方法返回类型返回值获取参数名称。1.如果方法返回类型为Object,则基于返回值获取参数名称2.如果方法返回类型非Object则基于返回类型获取参数名称

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
//Conventions.java
public static String getVariableNameForReturnType(Method method, Class<?> resolvedType, @Nullable Object value) {
//1.如果方法返回类型为Object
if (Object.class == resolvedType) {
//如果方法返回值为null,则抛出异常
if (value == null) {
throw new IllegalArgumentException(
"Cannot generate variable name for an Object return type with null value");
}
//调用getVariableName方法,根据返回值获取参数名,即根据返回值的真实类型获取参数名
return getVariableName(value);
}

//1.如果方法返回类型非Object类型
Class<?> valueClass;
boolean pluralize = false;
String reactiveSuffix = "";

//2.如果方法返回类型为数组类型
if (resolvedType.isArray()) {
//获取数组的元素类型
valueClass = resolvedType.getComponentType();
//标记复数形式命名
pluralize = true;
}
else if (Collection.class.isAssignableFrom(resolvedType)) {
//2.如果方法返回类型为Collection集合类型
//获取集合元素的类型
valueClass = ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric();
if (valueClass == null) {
if (!(value instanceof Collection)) {
throw new IllegalArgumentException("Cannot generate variable name " +
"for non-typed Collection return type and a non-Collection value");
}
Collection<?> collection = (Collection<?>) value;
if (collection.isEmpty()) {
throw new IllegalArgumentException("Cannot generate variable name " +
"for non-typed Collection return type and an empty Collection value");
}
Object valueToCheck = peekAhead(collection);
valueClass = getClassForValue(valueToCheck);
}
//标记复数命名
pluralize = true;
}
else {
//2.其它类型,则直接使用resolvedType方法返回类型
valueClass = resolvedType;
//获取响应式编程的适配器
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(valueClass);
//获取响应式编程返回值对应的后缀参数名
if (adapter != null && !adapter.getDescriptor().isNoValue()) {
reactiveSuffix = ClassUtils.getShortName(valueClass);
valueClass = ResolvableType.forMethodReturnType(method).getGeneric().toClass();
}
}

//3.根据方法返回类型class获取简短的参数名称
String name = ClassUtils.getShortNameAsProperty(valueClass);
//4.添加复数后缀List或者响应式后缀
return (pluralize ? pluralize(name) : name + reactiveSuffix);
}

image-20240412213031924

更新Model

updateModel方法

作用:1.将当前DefaultModel同步到SessionAttributes中,2.如果需要渲染页面,则给Model中相应参数设置BindingResult。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
//1.从mavContainer获取defaultModel对象
ModelMap defaultModel = container.getDefaultModel();

//2.如果SessionStatus#setComplete在处理器方法中调用了,则清空sessionAttributes
if (container.getSessionStatus().isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request);
}
else {
//2.没有调用SessionStatus#setComplete,则将defaultModel同步到sessionAttributes
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
}

//3. 更新BindingResult,即向defaultModel中添加BindingResult的相关参数(这里并没有校验的错误值)
if (!container.isRequestHandled() && container.getModel() == defaultModel) {
updateBindingResult(request, defaultModel);
}
}

在处理器中绑定参数时如果参数注释了@Valid或者@Validated,则会将校验结果设置到跟其相邻的下一个Error或者BindingResult类型的参数中

updateModel一共做了两件事,第一件事是维护SessionAttributes的数据,第二件是给Model中需要的参数设置BindingResult,以备视图使用。

3.6ServletInvocableHandlerMethod

image-20240412224139418

ServletInvocableHandlerMethod也是一种HandlerMethod,只是增加了方法执行的功能,当然也相应的增加了参数解析、返回值处理等相关功能

本节从HandlerMethod开始依次对这三个组件进行分析。

3.6.1HandlerMethod

用于封装Handler和其中具体处理请求的Method,分别对应其中的bean和method属性

如果Handler是String类型,将其变为容器中对应bean的过程在专门的方法createWithResolvedBean中来操作的。

HandlerMethod中属性的含义除了bridgedMethod外都比较容易理解,只是保存参数的属性parameters使用了可能不太熟悉的类型MethodParameter

MethodParameter里最重要的是method和parameterIndex,有了这两个参数后参数类型、注释等都可以获取到。不过在正常的反射技术里是不知道参数名的,所以这里专门使用了一个参数名查找的组件 parameterNameDiscoverer

在 HandlerMethod 中定义了两个内部类来封装参数,一个封装方法调用的参数HandlerMethodParameter,一个封装方法返回的参数ReturnValueMethodParameter

两个类都是MethodParameter的子类,而且ReturnValueMethodParameter还是HandlerMethodParameter的子类,其parameterIndex默认为-1。它们主要使用method和parameterIndex来创建MethodParamter,且它们使用的method都是bridgedMethod

bridge method(桥方法)

桥方法作为一个桥将Object为参数的调用转换到了调用String为参数的方法。

在HandlerMethod中的bridgedMethod指的是被桥的方法(注意是bridged而不是bridge),也就是原来的方法。如果不涉及泛型bridgedMethod和method都是同一个方法。

3.6.2InvocableHandlerMethod

继承自HandlerMethod,在父类的基础上添加了调用的功能(核心),也就是说,InvocableHandlerMethod可以直接调用内部属性method对应的方法(严格来说应该是bridgedMethod)。

方法调用是基于从http请求中解析出来的参数,和HandlerMethod中封装的method和bean来通过反射调用的

里面有三个属性:

  • dataBinderFactory:WebDataBinderFactory类型,可以创建WebDataBInder,用于参数解析器ArgumentResolver中。
  • resolvers:HandlerMethodArgumentResolverComposite类型,用于解析参数。
  • parameterNameDiscoverer:ParameterNameDiscoverer类型,用来获取参数名,用于MethodParameter中。(注意是在放法调用时解析参数时才将parameterNameDiscoverer从InvocableHandlerMethod设置到MethodParameter对象中)

1.核心调用方法为invokeForRequest

作用:getMethodArgumentValues解析方法参数(底层还是使用方法参数解析器组件解析),doInvoke调用方法

1
2
3
4
5
6
7
8
9
10
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//1.根据请求、mavContainer获取方法参数【难理解】
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//2.调用方法【核心】
return doInvoke(args);
}

2.doInvoke方法

作用:使用传入的方法参数执行handlermethod封装的方法。

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
protected Object doInvoke(Object... args) throws Exception {
//1.调用父类的getBridgedMethod方法获取原始方法反射对象
Method method = getBridgedMethod();
try {
if (KotlinDetector.isSuspendingFunction(method)) {
return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
}
//2.反射执行handlermethod封装的方法【核心】
return method.invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
//非法参数异常处理
assertTargetBean(method, getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
//方法调用异常处理
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}

3.getMethodArgumentValues(重要)

作用:使用请求、mavContainer、providedArgs(一般没有)获取方法参数数组。

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
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//1.获取方法参数数组(封装了方法反射对象和参数索引)
MethodParameter[] parameters = getMethodParameters();
//2.判断如果参数为空则直接返回空数组
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
//3.构建一个【参数值数组】,大小与parameters相同
Object[] args = new Object[parameters.length];
//4.遍历来初始化参数值数组元素
for (int i = 0; i < parameters.length; i++) {
//4.1获取当前索引的方法参数MethodParameter
MethodParameter parameter = parameters[i];
//4.2将参数名解析器从InvocableHandlerMethod设置到 MethodParameter 中
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
//4.3如果相应类型的参数已经在providedArgs提供,则直接设置到【参数值数组】args,且直接继续设置下一个方法参数
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//4.4校验参数解析器是否支持当前参数,如果不支持则抛出异常
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
//4.5使用参数解析器解析参数
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
//打印异常日志,抛出异常
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
//5.返回解析出的参数值数组
return args;
}

InvocableHandlerMethod就是在HandlerMethod基础上添加了方法调用功能,而方法调用又需要解析参数,所以又提供了解析参数的功能

解析的方法有两种,第一种是在providedArgs里面找,第二种是使用argumentResolvers解析,在RequestMappingHandlerAdapter中的调用并没有提供providedArgs,所以只有使用argumentResolvers解析

3.6.3ServletInvocableHandlerMethod

继承自InvocableHandlerMethod,在父类的基础上增加了三个功能:1.对@ResponseStatus注解的支持,2.对返回值的处理,3.对异步处理结果的处理。

对返回值的处理是使用returnValueHandlers属性完成的,它是HandlerMethodReturnValueHandlerComposite类型的属性。

当一个方法注解了@ResponseStatus后,返回的response会使用注释中的Status,如果处理器返回值为空或者注解的reason不为空,则将中断处理直接返回(不再渲染页面)。

1.invokeAndHandle方法(核心)

作用:1.invokeForRequest执行方法并获取方法返回值,2.setResponseStatus将解析的@ResponseStatus设置到响应对象中,3.@ResponseStatus注解时两个情况直接返回无需页面渲染(如果处理器返回值为空或者注解的reason不为空),4.返回值处理用于页面渲染

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
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//1.调用父类的invokeForRequest方法执行方法并获取方法返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//2.根据@ResponseStatus注解(在构造阶段就已经解析了@ResponseStatus注解设置值并设置到HandlerMethod中)设置响应对象的状态码和原因
setResponseStatus(webRequest);
//3.如果返回值为null
if (returnValue == null) {
//同时请求 NotModified为true(默认false) 或者 使用了@ResponseStatus注解 或者 请求已处理完,则直接返回
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
//在mavContainer设置请求处理完
mavContainer.setRequestHandled(true);
//直接返回
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
//3.如果@ResponseStatus注解设置了异常原因,则直接返回
mavContainer.setRequestHandled(true);
return;
}

//4.在mavContainer设置请求未处理完
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
//5.使用返回值处理器处理原始返回值
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}

2.setResponseStatus方法

作用:根据HandlerMethod解析好的@ResponseStatus注解内容设置响应对象。

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
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
//1.从父类 HandlerMethod 获取responseStatus属性
HttpStatus status = getResponseStatus();
//2.如果为空直接返回
if (status == null) {
return;
}
//3.获取响应对象
HttpServletResponse response = webRequest.getResponse();
//4.如果响应对象非空
if (response != null) {
//4.1从父类 HandlerMethod 获取responseStatusReason属性
String reason = getResponseStatusReason();
//4.2如果异常原因非空,则响应返回指定状态码与原因
if (StringUtils.hasText(reason)) {
response.sendError(status.value(), reason);
}
else {
//4.2如果异常原因为空,则设置响应状态码
response.setStatus(status.value());
}
}

//5.给请求对象设置属性,值为状态对象
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}

3.7HandlerMethodArgumentResolver

用来为处理器解析参数参数,主要用在前面讲过的InvocableHandlerMethod中。

HandlerMethodArgumentResolver接口定义,只有两个方法,一个用于判断是否可以解析传入的参数,另一个就是用于实际解析参数。

1
2
3
4
5
6
7
public interface HandlerMethodArgumentResolver {
//1.判断是否可以解析传入的参数
boolean supportsParameter(MethodParameter parameter);
//2.解析参数,返回的就是参数值
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

HandlerMethodArgumentResolver实现类一般有两种命名方式,一种是XXXMethodArgumentResolver,另一种是XXXMethodProcessor。前者表示一个参数解析器,后者除了可以解析参数外还可以处理相应类型的返回值,也就是同时还是后面要讲到的HandlerMethodReturnValueHandle。

另外还有个Adapter,它也不是直接解析参数的,而是用来兼容WebArgumentResolver类型的参数解析器的适配器。

解析器介绍:

  • AbstractMessageConverterMethodArgumentResolver:使用 HttpMessageConverter 解析request body类型参数的基类。其实现类有HttpEntityMethodProcessor、RequestPartMethodArgumentResolver、RequestResponseBodyMethodProcessor。
  • AbstractMessageConverterMethodProcessor:AbstractMessageConverterMethodArgumentResolver的扩展,支持返回值的处理。
  • HttpEntityMethodProcessor:解析HttpEntity(代表请求对象或者响应对象,包含头和体)和RequestEntity(代表请求对象,还包含请求url和请求类型)类型的参数
  • RequestResponseBodyMethodProcessor:解析注解了@RequestBody的参数(核心参数解析器)
  • RequestPartMethodArgumentResolver:解析注解了@RequestPart或者类型为MultipartFile类型以及javax.servlet.http.Part类型的参数
  • AbstractNamedValueMethodArgumentResolver:解析named value类型的参数(有name的参数,如cookie、requestParam、requestHeader、pathVariable等)的基类
  • AbstractCookieValueMethodArgumentResolver:是AbstractNamedValueMethodArgumentResolver的子抽象类,解析注解了@CookieValue的参数的基类
  • ServletCookieValueMethodArgumentResolver:是AbstractCookieValueMethodArgumentResolver的子类实现类,实现resolveName方法,具体解析cookieValue
  • ExpressionValueMethodArgumentResolver:解析注解了@Value的参数,解析过程在父类的resolveEmbeddedValuesAndExpressions方法完成。主要设置了beanFactory,并用它完成具体解析。
  • MatrixVariableMethodArgumentResolver:解析注解了@MatrixVariable而且类型不是Map的参数
  • PathVariableMethodArgumentResolver:解析注解了@PathVariable而且类型不是Map的参数
  • RequestHeaderMethodArgumentResolver:解析注解了@RequestHeader而且不是Map类型的参数
  • RequestParamMethodArgumentResolver:解析注解了**@RequestParam的参数MultipartFile类型的参数和没有注解的通用类型的参数,如果注解了@RequestParam且类型为Map的参数必须注解设置了name值否则不使用本解析器(核心参数解析器)**
  • ModelAttributeMethodProcessor:解析注解了@ModelAttribute的参数,如果其中的属性annotationNotRequired为true时还可以解析没有注解的非通用类型的参数(但默认为false)。
  • AbstractWebArgumentResolverAdapter:用作 WebArgumentResolver(接口,不同于HandlerMethodArgumentResolver接口) 解析器的适配器。
  • ErrorsMethodArgumentResolver:解析Errors类型的参数。当一个参数绑定出现异常时会自动将异常设置到其相邻的下一个Errors类型的参数,设置方法就是使用了这个解析器,内部是直接从model中获取的。
  • MapMethodProcessor:解析Map类型参数(包括ModelMap类型,且同时要求参数上没有任何注解)。直接返回mavContainer中的model作为参数值。
  • ModelMethodProcessor:解析Model类型参数。直接返回mavContainer中的model作为参数值。
  • RedirectAttributesMethodArgumentResolver:解析RedirectAttributes类型的参数。新建RedirectAttributesModelMap类型的RedirectAttributes并设置到mavConatiner中,然后返回其作为参数值。
  • ServletRequestMethodArgumentResolver:解析WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、Locale、TimeZone、InputStream、Reader、HttpMethod、ZoneId类型的参数,它们都是使用request获取的。
  • ServletResponseMethodArgumentResolver:解析ServletResponse、OutputStream、Writer类型的参数。它们都是使用response获取的。
  • SessionStatusMethodArgumentResolver:解析SessionStatus类型参数,直接返回mavContainer中的SessionStatus作为参数值。

1.ModelMethodProcessor源码分析

作用:用于解析Model类型的方法参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

@Override
public boolean supportsParameter(MethodParameter parameter) {
//支持解析 Model 类型的参数
return Model.class.isAssignableFrom(parameter.getParameterType());
}

@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
//解析参数值,直接返回 mavContainer 中的model
return mavContainer.getModel();
}
}

通过前面的分析知道,这时Model可能已经保存了一些值,如SessionAttributes中的值、FlashMap中的值、还有@ModelAttribute方法设置的值。主要是以下方法实现的初始化Model的值。

1
2
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);

2.PathVariableMethodArgumentResolver源码分析

作用:解析带@PathVariable注解的方法参数。特别的参数类型为Map时还同时要求@PathVariable注解设置了value值。

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
//AbstractNamedValueMethodArgumentResolver.java
//底层模板方法
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

//=====根据MethodParameter创建NamedValueInfo=======

//1.根据 parameter 上的注解来创建NamedValueInfo,封装了参数注解信息(还可能封装了参数名如果注解未设置名字的情况下)
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
//2.获取参数的真实 MethodParameter 对象
MethodParameter nestedParameter = parameter.nestedIfOptional();

//====================解析参数名===================

//3.解析 NamedValue 参数的名称。即通过注解的name属性值解析出参数名。name属性支持${}从配置文件获取,支持#{}SpEL表达式,如"#{systemProperties['java.vm.version']}"。
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
//4.如果参数名为空抛出异常
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}

//====================解析参数值===================

//5.通过子类 resolveName 方法解析参数值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
//6.如果参数值解析为空
if (arg == null) {
//6.1使用注解提供的默认值(如果注解提供了默认值)
if (namedValueInfo.defaultValue != null) {
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
//6.1如果注解要求必须提供,则抛出异常
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
//6.2处理参数值为空值的情况。Boolean类型参数设置值为false,基本类型参数抛出异常
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}

//===================参数类型转换===================

//7.如果 binderFactory 非空,则用它创建WebDataBinder并转换解析出的参数(如果需要转换)
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
// Check for null value after conversion of incoming argument value
if (arg == null && namedValueInfo.defaultValue == null &&
namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
}
}

//===================后置处理======================

//8.对解析出的参数进行后置处理。pathvariable中为设置请求参数,其它子类为空实现
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

return arg;
}

@Value中的value、

@PathVariable中的name、@RequestAttribute中的name、@SessionAttribute中的name、

@RequestHeader中的name和defaultValue、@RequestParam中的name和defaultValue、@CookieValue中的name和defaultValue支持${}与#{}SpEL表达式形式。

以上注解都由AbstractNamedValueMethodArgumentResolver子类来处理。

3.RequestParamMethodArgumentResolver源码分析

RequestMappingHandlerAdapter对象的argumentResolversz属性中包含对应类型的两个对象。第一个不会解析无注解简单类型参数,第二个会解析无注解简单类型参数。

作用

  1. 会解析带@RequestParam注解的参数
  2. 会解析无@RequestPart注解MultipartFile、Part类型(包括其数组类型和集合类型)的参数
  3. 会解析无@RequestParam注解简单类型的参数(包括基本数据类型、基本数据类型对应的包装类型、枚举类型、CharSequence字符序列类型、Date日期类型、Temporal类型即LocalDateTime日期相关类型、URI、URL、Locale、Class类型)。
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
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
implements UriComponentsContributor {

//判断是否支持解析指定参数的方法
public boolean supportsParameter(MethodParameter parameter) {
//1.如果参数带@RequestParam注解支持解析【情况一】
if (parameter.hasParameterAnnotation(RequestParam.class)) {
//1.1如果参数类型为Map类型,@RequestParam注解必须要指定name参数,如果没有指定则不支持而是依赖RequestParamMapMethodArgumentResolver来解析
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
//1.2获取参数上的@RequestParam注解
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
//1.3判断注解非空且指定了name参数
return (requestParam != null && StringUtils.hasText(requestParam.name()));
}
else {
//1.1如果为其它类型且带@RequestParam注解参数,一律支持
return true;
}
}
else {
//1.如果没有带@RequestParam注解的参数
//2.如果参数带@RequestPart注解不支持解析
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
//3.获取Optional类型参数的嵌套真实参数类型
parameter = parameter.nestedIfOptional();
//4.如果参数类型为MultipartFile、Part类型支持解析(隐含条件且不允许带@RequestPart、@RequestParam注解)【情况二】
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
else if (this.useDefaultResolution) {
//5.如果useDefaultResolution属性为true(第二个对象为true,第一个对象为false)
//如果参数类型为简单类型支持解析(隐含条件且不允许带@RequestParam、@RequestPart注解)【情况三】
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
//6.如果为其它情况则不支持解析
return false;
}
}
}

//根据参数名、参数类型、请求对象解析参数值(注意此时解析出的参数值为String类型)(父类AbstractNamedValueMethodArgumentResolver#resolveArgument方法调用)
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
//1.获取请求对象
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);

//2.解析MultipartFile、Part类型参数,并返回(如果不是此类型,则无效mpArg == MultipartResolutionDelegate.UNRESOLVABLE)【请求体获取方法参数值】
if (servletRequest != null) {
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
}

Object arg = null;
//3.解析Multipart类型请求
MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}

//4.如果还未解析到参数值(即不是流媒体数据类型MultipartFile、Part)(负责解析@RequestParam注解参数和不带@RequestParam注解简单类型参数)【核心】
if (arg == null) {
//4.1从请求参数中获取指定 参数名 的 请求参数值数组【请求参数中获取方法参数值】
String[] paramValues = request.getParameterValues(name);
//4.2如果请求参数值数组非空,将其作为方法参数值
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}

}

父类AbstractNamedValueMethodArgumentResolver关键方法

注意:MethodArgumentResolver里的resolveArgument方法的参数WebDataBinderFactory是由 InvocableHandlerMethod 的dataBinderFactory属性传入的,而其属性是在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
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
//AbstractNamedValueMethodArgumentResolver
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {

//HandlerMethodArgumentResolver解析参数【入口方法】
//根据参数类型MethodParameter、ModelAndViewContainer、请求对象、WebDataBinderFactory解析出参数值
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

//======获取并缓存指定方法参数对应的NamedValueInfo=========

//1.调用本类方法getNamedValueInfo获取NamedValueInfo,
//先查属性namedValueInfoCache缓存,查不到再调用子类createNamedValueInfo(parameter)创建NamedValueInfo类型对象并缓存起来(方法执行一次,后续方法就不用解析注解创建对象了)
//createNamedValueInfo(parameter)方法是根据注解信息来创建NamedValueInfo类型对象
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
//2.获取Optional参数类型的嵌套真实参数类型
MethodParameter nestedParameter = parameter.nestedIfOptional();

//=====================解析方法参数名===================

//3.根据namedValueInfo.name(来源于 注解 或者 方法参数名)解析方法参数名(可以解析嵌套表达式${属性文件值}或者SpEL表达式#{SpEL表达式})
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
//4.如果方法参数名为空则抛出异常
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}

//===================解析方法参数值=====================

//5.根据方法参数名、MethodParameter、请求对象解析方法参数值,调用子类的resolveName方法(不同子类实现不同,方法参数值来源不同,可能来自请求参数、请求头、请求属性、session、cookie、请求路径、@Value注解表达式)【核心】
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
//6.如果解析出方法参数值等于null
if (arg == null) {
//6.1namedValueInfo.defaultValue非空,即注解设置了默认值,则解析默认值作为方法参数值
if (namedValueInfo.defaultValue != null) {
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
//6.2如果必须要参数值且参数类型非Optional(隐含条件且没有设置注解默认值),调用子类handleMissingValue方法(缺失参数值时的方法),实现为抛出异常
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
//6.3处理为null值的方法参数(如果此时参数值仍然为null才会进行处理)
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
//6.如果解析出的方法参数值为""字符串且注解设置了默认值,则解析默认值作为方法参数值
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}

//=======================转换方法参数值类型===============

//7.如果binderFactory非空(方法传入,一般都非空)
if (binderFactory != null) {
//7.1使用 binderFactory 来创建WebDataBinder对象,【注意:target为null,objectName为方法参数名】,即创建的WebRequestDataBinder对象target属性等于null,objectName属性为方法参数名
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
//7.2调用binder来转换方法参数值从原始类型到指定类型
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
// Check for null value after conversion of incoming argument value
if (arg == null && namedValueInfo.defaultValue == null &&
namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
}
}

//8.解析后处理,可忽略不重要
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

//9.返回参数值(参数值表面类型为Object、真实类型为指定方法参数类型)
return arg;
}

//根据MethodParameter获取NamedValueInfo对象,此方法在方法调用中会逐步构建属性namedValueInfoCache缓存
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
//1.从属性namedValueInfoCache缓存中获取NamedValueInfo对象,第一次调用次方法时没有缓存后续都存在缓存
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
//2.如果缓存的namedValueInfo为空
if (NamedValueInfo == null) {
//2.1则调用子类createNamedValueInfo方法根据参数上的注解创建NamedValueInfo类型对象,如没有注解则创建一个默认对象(当然有些子类不允许没有注解)
namedValueInfo = createNamedValueInfo(parameter);
//2.2调用本类updateNamedValueInfo方法在子类创建的NamedValueInfo类型对象基础上再根据参数名创建新的NamedValueInfo对象
//可能会更新NamedValueInfo对象name属性、defaultValue属性
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
//2.3缓存到namedValueInfoCache属性
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
//3.返回NamedValueInfo对象(真实类型也为NamedValueInfo)
return namedValueInfo;
}

}

4.ServletModelAttributeMethodProcessor源码分析

RequestMappingHandlerAdapter对象的argumentResolvers属性中包含对应类型的两个对象。第一个不会解析无注解复杂类型参数,第二个会解析无注解复杂类型参数。

作用

  1. 支持解析带@ModelAttribute注解的方法参数
  2. 支持解析无@ModelAttribute注解非简单类型的方法参数

@ModelAttribute注解方法参数值解析来源,不仅来源于Model中还来源于 请求路径 和 请求参数,且请求路径优于请求参数优于Model。

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
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

//是否支持指定的方法参数MethodParameter
public boolean supportsParameter(MethodParameter parameter) {
//1.支持带@ModelAttribute注解的方法参数 或者 无@ModelAttribute注解非简单类型的方法参数
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}

//根据参数类型MethodParameter、ModelAndViewContainer、请求对象、WebDataBinderFactory解析参数值
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

//========================解析参数名====================

//1.调用ModelFactory静态方法getNameForParameter获取方法参数名
String name = ModelFactory.getNameForParameter(parameter);
//2.获取方法参数上的@ModelAttribute注解对象
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
//3.如果注解对象非空
if (ann != null) {
//设置 mavContainer 属性的绑定情况
mavContainer.setBinding(name, ann.binding());
}

//=================初步解析参数值===================

Object attribute = null;
BindingResult bindingResult = null;

//4.如果 mavContainer 中有与方法参数名同名的属性名
if (mavContainer.containsAttribute(name)) {
//4.1从model中获取同名属性值(会作为方法参数值)【情况一:从mavContainer的Model中获取方法参数值】
attribute = mavContainer.getModel().get(name);
}
else {
//4.如果 mavContainer 中没有有与方法参数名同名的属性名
//4.1调用子类方法createAttribute创建属性对象【情况二:直接创建指定类型的参数值对象,此时没有绑定对象属性】
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
else {
attribute = ex.getTarget();
}
bindingResult = ex.getBindingResult();
}
}

//=============绑定嵌套的参数值,并校验参数值==========

//5.如果 bindingResult 为空(异常绑定时不为空)【核心】
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
//5.1创建指定attribute对象的数据绑定对象binder,来绑定并校验对象中的属性值
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
//5.2数据绑定对象binder的目标对象target非空(此时为attribute)
if (binder.getTarget() != null) {
//5.2.1如果mavContainer没有禁止绑定此参数名
if (!mavContainer.isBindingDisabled(name)) {
//调用子类的bindRequestParameters方法,来通过request对象中的内容绑定对象属性数据
bindRequestParameters(binder, webRequest);
}
//5.2.2校验参数值
validateIfApplicable(binder, parameter);
//5.2.3校验异常则抛出异常
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
//5.3转换方法参数类型
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
//5.4获取绑定结果
bindingResult = binder.getBindingResult();
}

//6.更新mavContainer
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);

//7.返回参数值
return attribute;
}


}

ModelFactory.getNameForParameter(parameter)方法解析

作用:根据 MethodParameter对象 解析方法参数名,即基于参数上的注解或者参数类型解析参数名。

1
2
3
4
5
6
7
8
9
//ModelFactory.java
public static String getNameForParameter(MethodParameter parameter) {
//1.获取指定方法参数上的 @ModelAttribute 注解
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
//2.如果@ModelAttribute注解存在,则【基于注解value值】获取【方法参数名】
String name = (ann != null ? ann.value() : null);
//3.如果@ModelAttribute注解不存在,则【基于参数类型】获取【方法参数名】
return (StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter));
}

createAttribute(name, parameter, binderFactory, webRequest)方法解析

作用:根据参数名、参数类型创建方法参数值。(此时未初始化嵌套的参数属性值)。

1.对于**@ModelAttribute注解的参数,且未在mavContainer得Model中发现对应名称的属性时**,会在本方法创建Model的属性值,并作为方法参数值。(本子类实现如请求路径或请求参数存在此参数名的值时以此值作为方法参数值,父类实现不存在时创建Model属性对象)

2.对于没有@ModelAttribute注解非简单类型的参数,则会在本方法根据参数类型直接使用反射构造器创建方法参数值对象,后续再使用WebDataBinder来初始化方法参数的属性值。(完全由父类实现创建)

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
//ServletModelAttributeMethodProcessor.java
protected final Object createAttribute(String attributeName, MethodParameter parameter,
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {

//1.从请求对象中(请求路径、请求参数)获取与参数名同名的“请求值”【只使用与@ModelAttribute参数】
String value = getRequestValueForAttribute(attributeName, request);
//2.如果 “请求值” 非空,则基于”请求值“创建属性值/方法参数值,如果非空则返回
if (value != null) {
//2.1基于请求值通过WebDataBinder来转换值类型为方法参数值类型(一般只适用于@ModelAttribute注解的参数情况,因为WebDataBinder不支持直接将String转换为复杂对象类型)
Object attribute = createAttributeFromRequestValue(
value, attributeName, parameter, binderFactory, request);
//2.2如果 attribute非空,则返回类型转换好的方法参数值
if (attribute != null) {
return attribute;
}
}

//3.如果“请求值”为空,则调用父类createAttribute来创建属性值/方法参数值对象【适用于@ModelAttribute参数和复杂类型参数】
return super.createAttribute(attributeName, parameter, binderFactory, request);
}

//根据【方法参数名】从请求对象中获取“请求值”字符串
// * 先从【请求路径】获取指定方法参数名的 请求路径值
// * 再从【请求参数】中获取指定方法参数名的 请求参数值
// * 如果从以上参数值源中无法获取到,则返回null
protected String getRequestValueForAttribute(String attributeName, NativeWebRequest request) {
//1.获取请求路径名字与值的映射
Map<String, String> variables = getUriTemplateVariables(request);
//2.从请求路径中获取与【方法参数名】同名的【请求路径值】“请求值”,如果非空则返回
String variableValue = variables.get(attributeName);
if (StringUtils.hasText(variableValue)) {
return variableValue;
}
//3.获取与【方法参数名】同名的【请求参数值】,如果非空则返回
String parameterValue = request.getParameter(attributeName);
if (StringUtils.hasText(parameterValue)) {
return parameterValue;
}
//4.如果没有与方法参数名同名的请求路径与请求参数,则返回null
return null;
}

//根据String类型的 sourceValue 来创建属性值对象/方法参数值对象
protected Object createAttributeFromRequestValue(String sourceValue, String attributeName,
MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request)
throws Exception {

//1.从 binderFactory 获取 DataBinder 对象
DataBinder binder = binderFactory.createBinder(request, null, attributeName);
//2.从DataBinder对象获取 ConversionService 对象
ConversionService conversionService = binder.getConversionService();
//3.如果conversionService非空,尝试进行类型转换获取方法参数值
if (conversionService != null) {
//3.1获取源数据类型TypeDescriptor对象
TypeDescriptor source = TypeDescriptor.valueOf(String.class);
//3.2创建方法参数类型TypeDescriptor对象
TypeDescriptor target = new TypeDescriptor(parameter);
//3.3使用 conversionService 来转换数据类型,来获取正确类型的 方法参数值对象
if (conversionService.canConvert(source, target)) {
return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
}
}
//4.如果不支持类型转换则返回null(如:String类型=》自定义类型)
return null;
}

父类ModelAttributeMethodProcessor的createAttribute方法

作用:1.创建Model属性对象2.创建复杂类型对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//ModelAttributeMethodProcessor.java
protected Object createAttribute(String attributeName, MethodParameter parameter,
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {

//1.获取非Optional类型的 MethodParameter 参数类型对象
MethodParameter nestedParameter = parameter.nestedIfOptional();
//2.获取方法参数类型
Class<?> clazz = nestedParameter.getNestedParameterType();

//3.获取 方法参数反射构造器对象(可用的构造器对象)
Constructor<?> ctor = BeanUtils.getResolvableConstructor(clazz);
//4.通过构造器创建方法参数值对象 【核心】
Object attribute = constructAttribute(ctor, attributeName, parameter, binderFactory, webRequest);
if (parameter != nestedParameter) {
attribute = Optional.of(attribute);
}
//5.返回方法参数值对象
return attribute;
}

数据绑定器工厂

核心绑定不同类型参数的组件ServletRequestDataBinderFactory,每次调用方法就会创建一个此 WebDataBinderFactory 工厂对象。

image-20240420004623145

ServletRequestDataBinderFactory组件有两个作用:

  1. 对于简单类型,将String类型值(来源于请求)转换为方法参数值类型。
  2. 对于复杂类型,将string类型值(来源于请求)设置到方法参数对象的属性中。
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
//ServletRequestDataBinderFactory工厂对象的顶层设计
//主要实现了创建ExtendedServletRequestDataBinder对象
public class ServletRequestDataBinderFactory extends InitBinderDataBinderFactory {
//构造器
public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
@Nullable WebBindingInitializer initializer) {

super(binderMethods, initializer);
}

//获取 ExtendedServletRequestDataBinder类型的 DataBinder 对象(非单例的)
protected ServletRequestDataBinder createBinderInstance(
@Nullable Object target, String objectName, NativeWebRequest request) throws Exception {

return new ExtendedServletRequestDataBinder(target, objectName);
}

}

//主要存储了InitBinder方法集合、和实现了其相关方法
public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {

//InitBinderDataBinderFactory子类核心属性,封装了构造器传入的InitBinder方法对象集合
private final List<InvocableHandlerMethod> binderMethods;

//构造器,初始化binderMethods属性
public InitBinderDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
@Nullable WebBindingInitializer initializer) {

super(initializer);
this.binderMethods = (binderMethods != null ? binderMethods : Collections.emptyList());
}

//使用本对象的binderMethods属性来初始化传入的 WebDataBinder 参数对象
public void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception {
for (InvocableHandlerMethod binderMethod : this.binderMethods) {
if (isBinderMethodApplicable(binderMethod, dataBinder)) {
Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);
if (returnValue != null) {
throw new IllegalStateException(
"@InitBinder methods must not return a value (should be void): " + binderMethod);
}
}
}
}

//判断是否可以对指定WebDataBinder使用指定HandlerMethod
protected boolean isBinderMethodApplicable(HandlerMethod initBinderMethod, WebDataBinder dataBinder) {
InitBinder ann = initBinderMethod.getMethodAnnotation(InitBinder.class);
Assert.state(ann != null, "No InitBinder annotation");
String[] names = ann.value();
return (ObjectUtils.isEmpty(names) || ObjectUtils.containsElement(names, dataBinder.getObjectName()));
}

}

//主要存储了WebBindingInitializer对象,并实现了使用它来初始化WebRequestDataBinder对象
public class DefaultDataBinderFactory implements WebDataBinderFactory {

//初始化器属性(构造器传入)
@Nullable
private final WebBindingInitializer initializer;

//构造器
public DefaultDataBinderFactory(@Nullable WebBindingInitializer initializer) {
this.initializer = initializer;
}

//接口实现,底层模板方法
public final WebDataBinder createBinder(
NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {

//1.使用子类ServletRequestDataBinderFactory工厂对象方法创建WebDataBinder对象
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
//2.使用 initializer 属性对象中的属性来【初始化 dataBinder】的属性
if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);
}
//3.调用父类InitBinderDataBinderFactory的initBinder方法,使用initBinder方法集合来【初始化 dataBinder】
initBinder(dataBinder, webRequest);
return dataBinder;
}

}

//接口 WebDataBinderFactory
public interface WebDataBinderFactory {
//创建 WebDataBinder
//Params:
//webRequest – the current request
//target – the object to create a data binder for, or null if creating a binder for a simple type
//objectName – the name of the target object
WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName)
throws Exception;

}

数据绑定器

image-20240420005124008

核心入口方法是bind(ServletRequest request)方法

方法位于ServletRequestDataBinder类中。

作用:

  1. 收集【请求参数】中的数据创建 ServletRequestParameterPropertyValues 对象,
  2. 调用父类addBindValues(mpvs, request)方法收集【请求路径】中的数据填充ServletRequestParameterPropertyValues数据,
  3. 调用子类WebDataBinder的doBind(mpvs)方法来真正执行数据绑定。
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
//入口方法所在类(从请求对象执行数据绑定到目标参数对象(这里是此对象的target属性))
public class ServletRequestDataBinder extends WebDataBinder {

//入口方法。将【请求对象中的数据】绑定到【绑定器属性target中】。绑定支持multipart file
public void bind(ServletRequest request) {
//==============查询数据绑定源数据=============

//1.创建 ServletRequestParameterPropertyValues 对象,基于【请求参数数据】创建对象
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
//2.处理 文件类型的 方法参数数据绑定
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
else if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.MULTIPART_FORM_DATA_VALUE)) {
HttpServletRequest httpServletRequest = WebUtils.getNativeRequest(request, HttpServletRequest.class);
if (httpServletRequest != null && HttpMethod.POST.matches(httpServletRequest.getMethod())) {
StandardServletPartUtils.bindParts(httpServletRequest, mpvs, isBindEmptyMultipartFiles());
}
}
//3.基于【请求路径数据】将绑定数据源添加到创建的 mpvs 对象中
addBindValues(mpvs, request);

//=============使用mpvs来执行数据绑定==========

//4.使用收集到的绑定数据来真正执行数据绑定
doBind(mpvs);
}

}

WebDataBinder的doBind(mpvs)方法

作用:1.校验mpvs中的默认字段2.校验mpvs中的空白字段3.将mpvs中的[]名字后缀去掉4.真正执行数据绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//WebDataBinder.java
@Nullable
private String fieldMarkerPrefix = DEFAULT_FIELD_MARKER_PREFIX;

@Nullable
private String fieldDefaultPrefix = DEFAULT_FIELD_DEFAULT_PREFIX;

private boolean bindEmptyMultipartFiles = true;

protected void doBind(MutablePropertyValues mpvs) {
//1.从 mpvs 查找以 "!"开头的PropertyValue,并将其item移除,且可能会添加新的item。即检查有无默认值并视情况使用
checkFieldDefaults(mpvs);
//2.从 mpvs 查找以 "_"开头的PropertyValue,并将其item移除,且可能会添加新的item。即检查有无空白值并视情况使用
checkFieldMarkers(mpvs);
//3.从 mpvs 查找以 "[]"结束的PropertyValue,并将其item移除,且可能会添加新的item。
adaptEmptyArrayIndices(mpvs);
//4.调用父类DataBinder.dobind(mpvs)真正执行数据绑定
super.doBind(mpvs);
}

DataBinder的dobind(mpvs)方法

作用:1.校验是否允许绑定2.校验是否必须绑定3.指定数据绑定

是基于BeanWrapperImpl来实现的bean属性的数据类型转换,更核心的是AbstractNestablePropertyAccessor.java类。

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
protected void doBind(MutablePropertyValues mpvs) {
//1.通过本类属性allowedFields、disallowedFields来校验指定字段是否允许绑定
checkAllowedFields(mpvs);
//2.通过本类属性requiredFields来校验指定必须绑定的字段
checkRequiredFields(mpvs);
//3.真正执行数据绑定
applyPropertyValues(mpvs);
}

//真正执行数据绑定的我方法
protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
// Bind request parameters onto target object.
//1.首先从bindingResult属性中获取beanWrapper属性即得到ConfigurablePropertyAccessor(真实类型BeanWrapperImpl);然后调用其setPropertyValues方法【核心是BeanWrapperImpl类的实现】
getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
}
catch (PropertyBatchUpdateException ex) {
// Use bind error processor to create FieldErrors.
for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
}
}
}

protected ConfigurablePropertyAccessor getPropertyAccessor() {
return getInternalBindingResult().getPropertyAccessor();
}

protected AbstractPropertyBindingResult getInternalBindingResult() {
if (this.bindingResult == null) {
this.bindingResult = (this.directFieldAccess ?
createDirectFieldBindingResult(): createBeanPropertyBindingResult());
}
return this.bindingResult;
}

BeanWrapperImpl的核心转换数据类型是基于:ConversionService(TODO:分析BeanWrapperImpl、与WebConversionService组件)

对象参数转换核心:

1
2
3
4
5
6
7
//ConversionService接口
public interface ConversionService {
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
<T> T convert(@Nullable Object source, Class<T> targetType);
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
}

核心实现:

1
2
//GenericConversionService(WebConversionService父类)
//WebConversionService(WebMvcAutoConfiguration自动注入)

5.RequestResponseBodyMethodProcessor源码分析