SpringMVC(三):DispatcherServlet请求转发的实现

上一篇介绍的是init初始化过程,这次继续研究Servlet生命周期中的第二部分,service方法,在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过service()方法,委派到doGet()或者doPost()这些方法,完成Http请求的处理。

一、HttpServlet

首先看一下HttpServlet中的service方法:

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
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();

if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}

} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);

} else if (method.equals(METHOD_POST)) {
doPost(req, resp);

} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);

} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);

} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);

} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);

} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//

String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);

resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

根据不同的请求,它们分别被不同的方法处理。

二、FrameworkServlet

在FrameworkServlet中,处理各种请求的方法都被重写,

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
/**
* Delegate GET requests to processRequest/doService.
* <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
* with a {@code NoBodyResponse} that just captures the content length.
* @see #doService
* @see #doHead
*/
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}

/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}

/**
* Delegate PUT requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}

/**
* Delegate DELETE requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}

可以看出,这些方法都同意的调用了processRequest方法,而没有进行特殊的处理,注意一点这些方法都是final的,不会再被重写

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
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

long startTime = System.currentTimeMillis();
Throwable failureCause = null;

LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);

RequestAttributes previ eousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

initContextHolders(request, localeContext, requestAttributes);

try {
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}

finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}

if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}

publishRequestHandledEvent(request, response, startTime, failureCause);
}
}

protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;

整体上分为三个部分:

  1. 处理前的准备
  2. doService(request, response);进行处理
  3. 处理完成后的工作

1、处理过程

这部分有些地方和网上看见的或者书上看见的源码都不一样,应该是新版本进行了改进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
     LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);

RequestAttributes previ eousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

initContextHolders(request, localeContext, requestAttributes);

private void initContextHolders(
HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {

if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
}

不过大体的思路还是一样的,这里涉及几个关键的类和变量:

  • LocaleContext和RequestAttributes:
  • LocaleContextHolder和RequestContextHolder,持有上面的两个对象,通过get获得,处理之后通过set设置

下面看一下这两个holder对应的抽象类

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

private static final ThreadLocal<LocaleContext> localeContextHolder =
new NamedThreadLocal<>("LocaleContext");

private static final ThreadLocal<LocaleContext> inheritableLocaleContextHolder =
new NamedInheritableThreadLocal<>("LocaleContext");

// 下面的省略
}

public abstract class RequestContextHolder {

private static final boolean jsfPresent =
ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");

private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
// 下面的省略
}

他们内部都包含有ThreadLocal对象,所以,就可以知道整个过程的作用就是分别将这两个东西和请求线程做了绑定。

  1. 为了保证当前线程的LocaleContext和RequestAttributes可以在当前请求后还能恢复,提取当前线程的两个属性

  2. 根据当前的request创建对应的LocaleContext和RequestAttributes,并绑定到当前线程。

  3. doService(request, response)方法,按照一贯套路,肯定又是在子类中重写

  4. 请求处理结束后恢复线程到原始状态

  5. 请求处理结束后无论成功与否都发布事件通知。每次请求处理结束后,容器上下文都发布了一个ServletRequestHandledEvent事件,可以注册监听器来监听该事件。

可以看到,processRequest()方法只是做了一些线程安全的隔离,真正的请求处理,发生在doService()方法中。

三、DispatcherServlet

1、doService

doService方法在DispatcherServlet中被重写

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
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}

// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}

// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}

几个requet.setAttribute()方法的调用,将前面在初始化流程中实例化的对象设置到http请求的属性中,供下一步处理使用,其中有容器的上下文对象、本地化解析器等SpringMVC特有的编程元素。不同于Struts2中的ValueStack,SpringMVC的数据并没有从HttpServletRequest对象中抽离出来再存进另外一个编程元素,这也跟SpringMVC的设计思想有关。因为从一开始,SpringMVC的设计者就认为,不应该将请求处理过程和Web容器完全隔离。

2、doDispatch

接下来让我们看看doDispatch()这个整个请求转发流程中最核心的方法。DispatcherServlet所接收的Http请求,经过层层转发,最终都是汇总到这个方法中来进行最后的请求分发和处理。doDispatch()这个方法的内容,就是SpringMVC整个框架的精华所在。它通过高度抽象的接口,描述出了一个MVC(Model-View-Controller)设计模式的实现方案。Model、View、Controller三种层次的编程元素,在SpringMVC中都有大量的实现类,各种处理细节也是千差万别。但是,它们最后都是由,也都能由doDispatch()方法来统一描述,这就是接口和抽象的威力,万变不离其宗。

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
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
//step1
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

//step2
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
//step3
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

//step4
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

//step5
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

//step6
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

//step7
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

对应步骤:

  1. 对MultipartContent类型的Request处理
  2. 根据request信息寻找对应的Handler
  3. 没找到对应的Handler的错误处理
  4. 感觉当前Handler寻找对应的HandlerAdapter
  5. 缓存处理
  6. HandlerInterceptor的处理
  7. 逻辑处理
  8. 异常视图处理
  9. 根据视图跳转页面

下面主要分析几个关键的步骤

三、doDispatch

这个过程有几个问题不太明白再详细研究一下。

首先就是关于自己定义的Controller是怎么被执行的,这个要先明白Controller的定义方式,最原始的MVC的做法就是继承Controller接口,当然更高级的就是使用@Controller和@RequestMapping注解。这里先从Controller接口的方法分析,因为这个比较简单。定义一个bean

1
2
3
4
5
6
7
8
public class HomeAction implements Controller{  

@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
return new ModelAndView("hello");
}
}

1、HandlerMapping和HandlerAdapter

下面再来回顾一下SpringMVC处理请求的流程

用过python Django框架的都知道Django对于访问方式的配置就是,一个url路径和一个函数配对,你访问这个url,就会直接调用这个函数,简单明了。对于java的面向对象来说,就要分两步走。

  1. 第一步首先要找到是哪个对象,即handler,本工程的handler则是HomeAction对象。
  2. 第二步要找到访问的函数,即HomeAction的handleRequest方法。

所以就出现了两个接口HandlerMapping和HandlerAdapter,前者负责第一步,后者负责第二步

HandlerMapping的实现类:

  • BeanNameUrlHandlerMapping:通过对比url和bean的name找到对应的对象
  • SimpleUrlHandlerMapping :也是直接配置url和对应bean,比BeanNameUrlHandlerMapping功能更多
  • RequestMappingHandlerMapping :主要是针对注解配置@RequestMapping的,取代了最早的DefaultAnnotationHandlerMapping

HandlerAdapter的实现类:

  • SimpleControllerHandlerAdapter:要求handler实现Controller接口,该接口的方法为

    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response),

    也就是上面例子所采用的

  • HttpRequestHandlerAdapter : 要求handler实现HttpRequestHandler接口,该接口的方法为:

    void handleRequest(HttpServletRequest request, HttpServletResponse response)

    也就是 handler必须有一个handleRequest方法

  • RequestMappingHandlerAdapter : 和上面的RequestMappingHandlerMapping配对使用,针对@RequestMapping,取代了最早的AnnotationMethodHandlerAdapter。

先简单的说下这个工程的流程,访问http://localhost:8080/index首先由DispatcherServlet进行转发,通过BeanNameUrlHandlerMapping(含有 /index->HomeAction的配置),找到了HomeAction,然后再拿HomeAction和每个adapter进行适配,由于HomeAction实现了Controller接口,所以最终会有SimpleControllerHandlerAdapter来完成对HomeAction的handleRequest方法的调度。然后就顺利的执行了我们想要的方法。

了解了这些实现之后,再来看一下在源码中的实现。

2、HandlerExecutionChain的获取

2.1、getHandler方法

该过程对应的就是这一句

mappedHandler = getHandler(processedRequest);

相关的类在第一篇文章中已经介绍过了,有几点必须清楚:

  1. HandlerMapping是一个接口,DispatcherServlet中持有一个List< HandlerMapping>

  2. 获取HandlerExecutionChain的过程就是DispatcherServlet遍历这个List< HandlerMapping>的过程,并将第一个返回结果不为null作为得到的HandlerExecutionChain

  3. 上面的说法这个步骤叫作Handler的获取,但其实这个类是HandlerExecutionChain,只不过变量名与handler有关,真正的handler是封装在HandlerExecutionChain里面的。

    HandlerExecutionChain mappedHandler = null;

下面看一下具体的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

这个就是对应的遍历过程,而hm.getHandler(request)方法是在HandlerMapping接口中定义的,也就是说遍历的这个List中的所有实现类的对象都有这个方法,

2.2、BeanNameUrlHandlerMapping类与getHandler方法

BeanNameUrlHandlerMapping是前面提到的默认加载的HandlerMapping之一,来看一下它的类继承关系,

它的父父父类AbstractHandlerMapping实现了HandlerMapping接口,所以在这里可以看见对HandlerMapping接口方法getHandler的实现。

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
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {

// 上面的都省略

/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//1.根据request信息查找对应的handler
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 = getApplicationContext().getBean(handlerName);
}
//2.加入拦截器到执行链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}


//2.加入拦截器到执行链具体实现
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request){
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}

这个方法是final的,说明子类不会再重写,根据代码可以看出,该方法主要分两步:

  1. 根据request信息查找对应的handler

    这个gethandlerInternal方法主要就是根据url来获取对应的Handler,并且最后返回的handler是一个Object类型的对象。

  2. 加入拦截器到执行链

    前面介绍过HandlerExecutionChain中,封装了一个handler和n个拦截器,所以在找到对应的handler之后还要加入相应的拦截器。最后封装成HandlerExecutionChain。

经过两个步骤,实现根据输入的request到返回HandlerExecutionChain的过程。

3、HandlerAdapter的获取

3.1、getHandlerAdapter方法

该过程对应的是这句

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

与上面的方法非常类似,也是遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

遍历所有的HandlerAdapter,判断他们是否支持这个handler。 这里还是要找到默认配置中三个HandlerAdapter实现类中的一个,SimpleControllerHandlerAdapter,它要求handler实现Controller接口。

3.2、SimpleControllerHandlerAdapter类

因为它和HttpRequestHandlerAdapter的代码都不长,所以就一起贴出来了

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
package org.springframework.web.servlet.mvc;

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

@Override
public boolean supports(Object handler) {
//就是判断handler是否实现了Controller接口
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;
}
}

public class HttpRequestHandlerAdapter implements HandlerAdapter {

@Override
public boolean supports(Object handler) {
//就是判断handler是否实现了HttpRequestHandler接口
return (handler instanceof HttpRequestHandler);
}

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//若handler实现了HttpRequestHandler接口,则调用该接口的方法,执行我们在该方法中写的业务逻辑
((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;
}

}

可以看出它们的各个supports方法的唯一区别就是,不同的适配器支持不同的传入的handler类型。

所以再来看一下这个过程就是遍历所有的适配器,找到第一个支持这个handler的适配器,然后返回。

从上面的例子来看,SimpleControllerHandlerAdapter是支持HomeAction的,所以就会返回这个适配器,然后通过它来执行下面的handle方法,也就进入了自己定义的逻辑之中。

3.3、适配器模式

到这里更加理解这个适配器模式了,这个handler的类型有很多种,而最上面的HandlerExecutionChain持有的时候并不知道它是什么类型,因为是Object类型的,它可能是Controller类,也有可能是HttpRequestHandler类,而这些不同的实现类具体的实现方法肯定是不同的,所以,每个实现类都有一个对应的适配器类,得到这个handler的时候遍历所有的适配器,遇到适配器支持的实现类,则返回这个适配器,然后通过这个适配器来调用具体实现类中的方法。

这样对于DispatcherServlet来说,它不知道每个handler具体的实现过程和方法,它需要的就是support和handle方法,至于具体对应到实现类中的哪个方法,则由适配器来决定。

而这个handle方法,则是第7步对应的逻辑处理了。

4、handle方法

在适配器中,handle方法被转换成handler的handleRequest方法。并且返回一个ModelAndView类对象,这里就是执行用户定义的Controller的地方了。

所以这里针对不同方式定义的Controller,执行的方式也是不同的。

四、参考地址

https://my.oschina.net/lichhao/blog/104943

http://blog.csdn.net/zljjava/article/details/50414585