SpringMVC 解毒3

4 HandlerAdapter

第三章将 HandlerMapping的时候,我们看到抽象类 AbstractUrlHandlerMapping 中的映射的handler都是Object类型的,而抽象类 AbstractHandlerMethodMapping 的最终实现类的handler必须是方法,类必须有@Controller@RequestMapping注解,对应的方法也得有@RequestMapping注解。

所以说 AbstractHandlerMethodMapping 的最终实现类生成的handler应该有特定的 HandlerAdapter 实现类去运行,而 AbstractUrlHandlerMapping 的最终实现类生成的handler,如果没有使用标准的接口或抽象类,那么就没有对应的 HandlerAdapter,原因是SpringMVC并不知道你的类应该运行哪个方法,怎么设置参数等等。

接下来让我们来认识认识 HandlerAdapter 吧。

4.1 HandlerAdapter 接口

HandlerAdapter 接口实际上有三个方法,我们比较关注的是其中两个。

第一个是supports方法,在2.4.2节中,我们看到在DispatcherServlet中doDispatch方法执行时,如果拿到了持有handler的执行链,会循环调用每一个 HandlerAdapter 的supports方法判断当前的 HandlerAdapter 实现类是否支持这个handler,如果支持,就返回该 HandlerAdapter 实现类。最后,如果都没有支持的,就会抛出异常。

第二个是handle方法,还是在2.4.2节,通过第一个方法得到了handler对应的adpater之后,会先调用执行链的前处理方法,在3.1节中有讲到是如何调用的。调用完前处理方法后,会用找到的adapter调用handle方法返回 ModelAndView。

以下的几个小节都会围绕这个两个方法展开。

还有,为了对应 HandlerMapping 中的顺序,HandlerAdapter 子类也是有顺序的。

4.2 SimpleServletHandlerAdapter 分析

说实话 SimpleServletHandlerAdapter 实在没什么分析的。但是!!!,你一定要理解我在 HandlerMapping 分析中讲的,handler除了在 AbstractHandlerMethodMapping 是被SpringMVC规范的,其他handler都是object,具体怎么用全靠adapter说了算。

好了,直接看源码吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SimpleServletHandlerAdapter implements HandlerAdapter {

@Override
public boolean supports(Object handler) {
return (handler instanceof Servlet);
}

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

((Servlet) handler).service(request, response);
return null;
}

@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}

}

惊不惊喜?意不意外?就这么简单,如果你给 AbstractUrlHandlerMapping 最终实现类中设置的bean是实现Servlet接口的或者间接实现Servlet接口的,那么他会直接调用Servlet接口的service方法。

4.3 HttpRequestHandlerAdapter 分析

哇,这个类了不起啊,这个类几乎和4.2节 SimpleServletHandlerAdapter 一模一样。它对应的接口是 HttpRequestHandler,这个接口也了不起,除了没有init和destroy方法,其他和Servlet接口差不多。HttpRequestHandler 接口是SpringMVC自定义的一个接口,入参是request和response,具体这个接口怎么使用,后面的章节会讲到,但是我在这里告诉你关于静态资源文件的映射的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
public class HttpRequestHandlerAdapter implements HandlerAdapter {

@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}

@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}

}

4.4 SimpleControllerHandlerAdapter 分析

经过前两个类分析,可以总结出一个规律,那就是使用 AbstractUrlHandlerMapping 基于类的URI映射都是比较简单的。那么这个类也一样,但是需要额外提一下的是 HttpRequestHandlerAdapter 跟我们的业务逻辑handler关系不大,但你写业务逻辑的handler可能就会用到 SimpleControllerHandlerAdapter。因为这个类对应的handler,其实现的接口是Controller,注意是接口不是注解,在最开始学些SpringMVC开发时,你可能接触过。

以下是类代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SimpleControllerHandlerAdapter implements HandlerAdapter {

@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

return ((Controller) handler).handleRequest(request, response);
}

@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}

}

4.5 AbstractHandlerMethodAdapter 分析

前边三个adapter实现类都很容易理解,但是接下来的实现类都是基于方法映射的了,有点难度,大家打起精神来,攻克adapter最后的难关。

AbstractHandlerMethodAdapter 抽象类相比 AbstractHandlerMethodMapping 抽象类要简单的多,原因是mapping需要指导映射规则生成和匹配,而adapter这边只需要支持方法执行。

实际上 AbstractHandlerMethodAdapter 只增加了可排序接口,并且指定了handler最起码需要是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
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
* @param handler the handler instance to check
* @return whether or not this adapter can adapt the given handler
*/
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

/**
* Given a handler method, return whether or not this adapter can support it.
* @param handlerMethod the handler method to check
* @return whether or not this adapter can adapt the given method
*/
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);

/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}

/**
* Use the given handler method to handle the request.
* @param request current HTTP request
* @param response current HTTP response
* @param handlerMethod handler method to use. This object must have previously been passed to the
* {@link #supportsInternal(HandlerMethod)} this interface, which must have returned {@code true}.
* @return ModelAndView object with the name of the view and the required model data,
* or {@code null} if the request has been handled directly
* @throws Exception in case of errors
*/
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;

4.6 RequestMappingHandlerAdapter 分析

到这了务必打起精神,因为这块的难度可能要比DispatcherServlet还要难,这里是SpringMVC除了DispatcherServlet外最精髓的地方,可以和bean创建的代码一较高低。由于在这个类中引入了比较多的接口和类,所以我先带大家简单梳理一下用到的接口和类。这些接口和类后面的章节也会讲到,这里只不过是提前熟悉一下。

4.6.1 HandlerMethodArgumentResolver 分析

这个接口是用来解析方法入参的,能够将从request和配置中的信息解析为方法入参。而 HandlerMethodArgumentResolverComposite 则是该接口的一个实现类,它的作用是代理所有的接口实现类。向 HandlerMethodArgumentResolverComposite 添加一组方法入参解析器,而后只调用 HandlerMethodArgumentResolverComposite 的相关接口方法,HandlerMethodArgumentResolverComposite 会自动匹配它包含的所有解析器中最合适的,并代理执行。

4.6.2 HandlerMethodReturnValueHandler 分析

HandlerMethodReturnValueHandler 接口用来处理方法的返回参数。HandlerMethodReturnValueHandlerComposite 则是该接口的一个实现类,它的作用是代理一组 HandlerMethodReturnValueHandler 接口实现类,挑选最合适的实现类执行。

4.6.3 HttpMessageConverter 分析

HttpMessageConverter 是用来解析request中的数据和向response中写入数据的。

4.6.4 ParameterNameDiscoverer 分析

ParameterNameDiscoverer 接口是从方法和构造函数中获取参数名的。别以为参数名好搞,你在IDE中写的方法入参名假设是methodParameter1,等编译完毕,你再用IDE的反编译功能反编译一下,会发现方法入参名变成var1/var2之类的了。

所以,Spring采用的办法是使用ASM字节码操作框架直接从本地变量表中获取方法入参名,相关的类是LocalVariableTableParameterNameDiscoverer

而从JDK1.8起,JDK中自带了java.lang.reflect.Parameter类,可以直接从这里获取方法入参名,所以Spring会先判断JVM使用的版本环境,如果包含java.lang.reflect.Executable类,说明是JDK1.8,就默认使用StandardReflectionParameterNameDiscoverer类获取变量,而不是用ASM字节码操作框架。

4.6.5 ModelAndViewContainer 分析

ModelAndViewContainer 是 ModelAndView 产生之前的包装器,例如方法返回了model,则需要判断是否自动添加view,如果返回了view,需要生成一个model出来等等。

4.6.6 ServletInvocableHandlerMethod 和 InvocableHandlerMethod 分析

还记得3.8.1节讲到的 HandlerMethod 吗?HandlerMethod 只是一些数据的组合,而 ServletInvocableHandlerMethod 和 InvocableHandlerMethod 则给 HandlerMethod 装上了方法,使 HandlerMethod 中的method、bean等数据可以被调用。

4.6.7 RequestMappingHandlerAdapter 初始化分析

由于要处理request解析入参、方法反射调用、返回结果处理等等事情,所以这个类初始化的东西也很多。我们来找一找初始化点吧。

1
2
3
4
5
6
7
8
9
10
public RequestMappingHandlerAdapter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316

this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

首先第一个就是构造函数,在构造函数中,初始化了消息转换器。

  • StringHttpMessageConverter的作用是把body部分的字节数组转化为String,将String类型的数据写入到response的outputStream中。
  • ByteArrayHttpMessageConverter的作用是把body部分的字节数组转到新的字节数组中,将字节数组写入到response的outputStream中。
  • SourceHttpMessageConverter的作用是body部分的字节数组转化为xml对象,将xml对象写入到response的outputStream中。
  • AllEncompassingFormHttpMessageConverter的作用是将body中xml或json部分数据转化为对象,将多个xml对象或多个json对象写入到response的outputStream中。
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
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}

/**
* Return the list of argument resolvers to use including built-in resolvers
* and custom resolvers provided via {@link #setCustomArgumentResolvers}.
*/
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}


/**
* Return the list of return value handlers to use including built-in and
* custom handlers provided via {@link #setReturnValueHandlers}.
*/
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
handlers.add(new StreamingResponseBodyReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
// Annotation-based return value types
handlers.add(new ModelAttributeMethodProcessor(false));
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
}
else {
handlers.add(new ModelAttributeMethodProcessor(true));
}
return handlers;
}

第二个初始化点在 afterPropertiesSet 方法。在这个方法中,我们重点关注两个内容,一个是方法入参解析器加载,另一个是返回参数处理器加载。

在方法入参解析器加载中,把解析器分为四种类型,第一种是基于注解的解析器,第二种是基于类型的解析器,第三种是通用解析器,最后一种是默认解析器。

在返回结果处理器加载中,把处理器分为五种类型,第一种是单目的类型处理器,第二种是使用注解类型处理器,第三种是多目的类型处理器,第四种是通用类型处理器,最后一种是默认处理器。

在生成这些解析器处理器时,有的构造方法引入了web应用上下文,有的引入了属性messageConverters。能说明什么问题呢?说明这些解析器处理器需要从web应用上下文中获取bean供它们使用,需要使用messageConverter对入参或者返回结果进行转换。至于这些解析器处理器分别都是干什么的,目前先不展开,否则你会迷失在 RequestMappingHandlerAdapter。

4.6.8 RequestMappingHandlerAdapter 处理请求分析

前面提到 AbstractHandlerMethodAdapter 抽象类只确定了handler必须是HandlerMethd类型,其余的都留给子类实现。

RequestMappingHandlerAdapter 中,针对判断是不是handler直接返回了true,说明handler只要是HandlerMethd类型的,都可以处理。

下面是重写的handleInternal方法

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
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}

我们看到重写的handleInternal又调用了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
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
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();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

代码从第11行到第28行都是在做准备工作,除开异步响应相关代码,主要内容有初始化 ServletInvocableHandlerMethod 对象,初始化 ModelAndViewContainer 对象。可以看到,在 RequestMappingHandlerAdapter 初始化的属性:方法入参解析器代理类,返回结果处理器代理类、方法入参名探索器等等。

在第38行则调用了 ServletInvocableHandlerMethod 对象的invokeAndHandle方法,这个方法应该干的就是:方法入参解析、反射方法调用、返回结果处理等。

在第42行,则是调用getModelAndView方法生成ModelAndView,假如方法返回null、方法中已经向response写入数据、返回结果处理器向response写入数据,都返回null,也就是不需要 ViewResolver 进行渲染。

既然大致步骤我们都知道了,接下来就是休闲时刻,来看看反射方法调用吧。

ServletInvocableHandlerMethod.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
/**
* Invokes the method and handles the return value through one of the
* configured {@link HandlerMethodReturnValueHandler}s.
* @param webRequest the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type (not resolved)
*/
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}

在这个方法里可以看到,如果返回参数为null且已经响应response,或者mavContainer属性responseReason不为空,则设置mavContainer属性requestHandled为true,可见,如果属性requestHandled为true,adpater就不会生成 ModelAndView。

执行invokeForRequest方法得到返回结果后,会调用returnValueHandlers对结果进行处理,这个处理可能是添加模型数据,又或者是直接写出数据等等。

接下来看invokeForRequest方法吧

InvocableHandlerMethod.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
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
/**
* Invoke the method after resolving its argument values in the context of the given request.
* <p>Argument values are commonly resolved through {@link HandlerMethodArgumentResolver}s.
* The {@code providedArgs} parameter however may supply argument values to be used directly,
* i.e. without argument resolution. Examples of provided argument values include a
* {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
* Provided argument values are checked before argument resolvers.
* @param request the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type, not resolved
* @return the raw value returned by the invoked method
* @exception Exception raised if no suitable argument resolver can be found,
* or if the method raised an exception
*/
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
}
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
return returnValue;
}

/**
* Get the method argument values for the current request.
*/
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
throw new IllegalStateException(msg);
}
}
return args;
}


/**
* Invoke the handler method with the given argument values.
*/
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(getInvocationErrorMessage(message, 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 {
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
throw new IllegalStateException(msg, targetException);
}
}
}

操作相当简单, 先通过parameterNameDiscoverer获取方法的入参名,再通过argumentResolvers获取入参,设置方法为可访问方法,反射调用方法,返回结果。

4.6.9 小结

RequestMappingHandlerAdapter 之所以比其他adpater复杂,不仅仅是因为它是基于方法的映射,前面的adpater只能根据URI进行映射,而 RequestMappingHandlerAdapter 还同时可以根据Header内容进行规则匹配映射。

从request到方法入参,再到执行,再到返回结果处理,RequestMappingHandlerAdapter 设计了一套精密的结构将这些功能组合到一起,如果让我们自己来写一个基于方法映射的处理器适配器,我觉得难度不是一点点,最起码要耗费比较长的时间才能开发完。

4.7 小结

HandlerAdapter 它是不用管映射规则的,只用管从 HandlerMapping 中获取到的handler,尽管去执行就好,所以是不是感觉模块划分还算比较合理?要不然映射handler和处理handler放到一起写,那不得愁死了,既要管映射规则,还要管不同的handler不同的处理方式。还要注意一点,handle方法要么返回 ModelAndView 要么返回null,如果返回的是 ModelAndView,那 ViewResolver 必须处理,处理不了就得报错。还有一点,如果你自定义了handler,那么也得自定义一个 HandlerAdapter,要不然对应不上 adapter就会抛出异常。

HandlerAdapter 就先讲到这里了,大家是不是对 ModelAndView 是如何转为视图渲染的更加有兴趣了?也更加有信心了?