首先看下面这种张图,这张图说明了spring mvc整体的流程。
本文讲的就是如何从DispatcherServlet中得到ModerAndView的过程。
首先看DispatherServlet这个类的doService方法,学过servlet的人都知道,它是web容器处理请求的入口。
1 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
2 if (logger.isDebugEnabled()) {
3 String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
4 logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
5 " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
6 }
7
8 // Keep a snapshot of the request attributes in case of an include,
9 // to be able to restore the original attributes after the include.
10 Map<String, Object> attributesSnapshot = null;
11 if (WebUtils.isIncludeRequest(request)) {
12 attributesSnapshot = new HashMap<String, Object>();
13 Enumeration<?> attrNames = request.getAttributeNames();
14 while (attrNames.hasMoreElements()) {
15 String attrName = (String) attrNames.nextElement();
16 if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
17 attributesSnapshot.put(attrName, request.getAttribute(attrName));
18 }
19 }
20 }
21
22 // Make framework objects available to handlers and view objects.
23 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
24 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
25 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
26 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
27
28 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
29 if (inputFlashMap != null) {
30 request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
31 }
32 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
33 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
34
35 try {
36 doDispatch(request, response);
37 }
38 finally {
39 if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
40 return;
41 }
42 // Restore the original attribute snapshot, in case of an include.
43 if (attributesSnapshot != null) {
44 restoreAttributesAfterInclude(request, attributesSnapshot);
45 }
46 }
47 }
可以看到,doService做的事情一共就两件,一是保存request的Attribute并在框架处理完之后恢复回去,以防止attribute在框架处理的过程中被迫坏;第二件事就是在第36行调用doDispatch,进入真正的spring mvc流程中。我们来看doDispatch函数,这个函数是DispatchServlet控制整个spring mvc流程的核心,文章开始的那张图所描述的流程都是在这个函数中完成的。我们来看一下这个函数。
1 /**
2 * Process the actual dispatching to the handler.
3 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
4 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
5 * to find the first that supports the handler class.
6 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
7 * themselves to decide which methods are acceptable.
8 * @param request current HTTP request
9 * @param response current HTTP response
10 * @throws Exception in case of any kind of processing failure
11 */
12 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
13 HttpServletRequest processedRequest = request;
14 HandlerExecutionChain mappedHandler = null;
15 boolean multipartRequestParsed = false;
16
17 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
18
19 try {
20 ModelAndView mv = null;
21 Exception dispatchException = null;
22
23 try {
24 processedRequest = checkMultipart(request);
25 multipartRequestParsed = (processedRequest != request);
26
27 // Determine handler for the current request.
28 mappedHandler = getHandler(processedRequest);
29 if (mappedHandler == null || mappedHandler.getHandler() == null) {
30 noHandlerFound(processedRequest, response);
31 return;
32 }
33
34 // Determine handler adapter for the current request.
35 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
36
37 // Process last-modified header, if supported by the handler.
38 String method = request.getMethod();
39 boolean isGet = "GET".equals(method);
40 if (isGet || "HEAD".equals(method)) {
41 long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
42 if (logger.isDebugEnabled()) {
43 logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
44 }
45 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
46 return;
47 }
48 }
49
50 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
51 return;
52 }
53
54 try {
55 // Actually invoke the handler.
56 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
57 }
58 finally {
59 if (asyncManager.isConcurrentHandlingStarted()) {
60 return;
61 }
62 }
63
64 applyDefaultViewName(request, mv);
65 mappedHandler.applyPostHandle(processedRequest, response, mv);
66 }
67 catch (Exception ex) {
68 dispatchException = ex;
69 }
70 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
71 }
72 catch (Exception ex) {
73 triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
74 }
75 catch (Error err) {
76 triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
77 }
78 finally {
79 if (asyncManager.isConcurrentHandlingStarted()) {
80 // Instead of postHandle and afterCompletion
81 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
82 return;
83 }
84 // Clean up any resources used by a multipart request.
85 if (multipartRequestParsed) {
86 cleanupMultipart(processedRequest);
87 }
88 }
89 }
这个方法的核心工作内容包括这么几件:处理http请求得到模型和视图(存在一个ModelAndView对象中)、处理拦截器、渲染视图、卸载multipart内容,以及获取用于处理模型所需的处理器HanderExecutionChain和适配器HandlerAdapter。
http的请求具体会被怎么处理将取决于处理器执行器HanderExecutionChain和适配器HandlerAdapter,所以我们先看这两个对象是怎么得到的。方法注释的已经大致说明了这两个对象是如何得到的:HanderExecutionChain是通过应用servlet所拥有的HanderMapping生成的,HandlerAdapter是通过依次查询servlet所拥有的适配器中能够支持执行器的适配器得到的。
那么我们就来看一下HanderExecutionChain和HandlerAdapter到底是怎么得到的。
看28行的getHandler,HanderExecutionChain是从这里获取的,跟进去看:
1 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
2 for (HandlerMapping hm : this.handlerMappings) {
3 if (logger.isTraceEnabled()) {
4 logger.trace(
5 "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
6 }
7 HandlerExecutionChain handler = hm.getHandler(request);
8 if (handler != null) {
9 return handler;
10 }
11 }
12 return null;
13 }
看第7行,真的是通过DistpacherServlet的HandlerMappings得到的,跟进去看怎么得到的:
发现这个servlet拥有的一个HM是AbstractHanderMethondMapping对象,跟进他的方法,发现它在第3行获取了一个HandlerMethod(名为handler的object对象)。
1 @Override
2 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
3 Object handler = getHandlerInternal(request);
4 if (handler == null) {
5 handler = getDefaultHandler();
6 }
7 if (handler == null) {
8 return null;
9 }
10 // Bean name or resolved handler?
11 if (handler instanceof String) {
12 String handlerName = (String) handler;
13 handler = getApplicationContext().getBean(handlerName);
14 }
15 return getHandlerExecutionChain(handler, request);
16 }
这个HandlerMethod对象存的是什么呢?我们看看:
原来是它封装了我们映射到的控制器,包括bean,以及map到的方法。有了这个封装对象,剩下的事情就好办了。
继续往下看便到了第15行,跟进去getHandlerExecutionChain方法:
1 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
2 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
3 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
4 chain.addInterceptors(getAdaptedInterceptors());
5
6 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
7 for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
8 if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
9 chain.addInterceptor(mappedInterceptor.getInterceptor());
10 }
11 }
12
13 return chain;
14 }
可以看到,这个方法new了HandlerExecutionChain对象,并且把HM的拦截器和用户的拦截去都加进去了。至此HandlerExecutionChain的获取过程就讲完了。
接下来看适配器HandlerAdapter是怎么来的。
1 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
2 for (HandlerAdapter ha : this.handlerAdapters) {
3 if (logger.isTraceEnabled()) {
4 logger.trace("Testing handler adapter [" + ha + "]");
5 }
6 if (ha.supports(handler)) {
7 return ha;
8 }
9 }
10 throw new ServletException("No adapter for handler [" + handler +
11 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
12 }
真的就是看servlet有那些适配器,然后一个个查询是否支持,最后返回。
HanderExecutionChain和HandlerAdapter都有了,那么接下来就要看它们是怎么获取到模型和视图了。
跟进去dispatch方法的56行,一路下去,来到了 ModelAndView invokeHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)
1 private ModelAndView invokeHandleMethod(HttpServletRequest request,
2 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
3
4 ServletWebRequest webRequest = new ServletWebRequest(request, response);
5
6 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
7 ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
8 ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
9
10 ModelAndViewContainer mavContainer = new ModelAndViewContainer();
11 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
12 modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
13 mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
14
15 AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
16 asyncWebRequest.setTimeout(this.asyncRequestTimeout);
17
18 final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
19 asyncManager.setTaskExecutor(this.taskExecutor);
20 asyncManager.setAsyncWebRequest(asyncWebRequest);
21 asyncManager.registerCallableInterceptors(this.callableInterceptors);
22 asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
23
24 if (asyncManager.hasConcurrentResult()) {
25 Object result = asyncManager.getConcurrentResult();
26 mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
27 asyncManager.clearConcurrentResult();
28
29 if (logger.isDebugEnabled()) {
30 logger.debug("Found concurrent result value [" + result + "]");
31 }
32 requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
33 }
34
35 requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
36
37 if (asyncManager.isConcurrentHandlingStarted()) {
38 return null;
39 }
40
41 return getModelAndView(mavContainer, modelFactory, webRequest);
42 }
终于知道为什么需要适配器了,原来这个适配器的方法中,就适配了spring mvc和servlet,还有几个适配对象,其中最重要的是ServletWebRequest webRequest ,它适配了request。另外还有ModelAndViewContainer mavContainer,通过它持有模型和视图。
另外看这三行,这三行采用了工厂模式。
第一行获取了数据绑定的工厂,最重要的是它有handlerMethod。然后它会传给第三行,得到ServletInvocableHandlerMethod requestMappingMethod 。这个 requestMappingMethod 就是我们进入控制器调用的关键。
第二行获取了模型工厂,有了它,就可以创建模型了。
值得注意的是,这三个对象,每个里面都有handlerMethod的信息。
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
看第35行,requestMappingMethod.invokeAndHandle(webRequest, mavContainer),这里开始真正的填充模型了,怎么填充呢?其实猜都能猜到,首先它自己有handlermethod,又传入了webRequest和mavContainer,很容易想到,它肯定是根据请求,通过反射获取控制器的requestMap方法,将参数传入并调用方法,最后得到控制器处理后的模型,并得到控制器指定的视图。至于传入的参数是如何得到的,我在另一篇文章李有比较详细的描述,请看spring mvc中的控制器方法中的参数从哪里传进来这篇文章。
看起来貌似已经把整个流程讲完了,但是等等,有个很重要的问题,生成处理器执行器HandlerExecutionChain的时候需要从HM里get到一个真正的处理器传入给HandlerExecutionChain的构造器。这个处理器怎么来的?这个带着这个疑问,我们继续来看他们是怎么得到的。
前面已经说了,这个handler是在HandlerExecutionChain getHandler(HttpServletRequest request) 的第三行调用HM的Object handler = getHandlerInternal(request)得到的,我们看一看getHandlerInternal这个函数:
1 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
2 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
3 if (logger.isDebugEnabled()) {
4 logger.debug("Looking up handler method for path " + lookupPath);
5 }
6 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
7 if (logger.isDebugEnabled()) {
8 if (handlerMethod != null) {
9 logger.debug("Returning handler method [" + handlerMethod + "]");
10 }
11 else {
12 logger.debug("Did not find handler method for [" + lookupPath + "]");
13 }
14 }
15 return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
16 }
可以看到,getHandlerInternal是通过请求的路径来查询得到那个关键的处理器(在这里是一个HandlerMethod,它含有控制器的requestMap方法签名),再看一下第6行,它是怎么通过路径查询到处理器的,跟进去lookupHandlerMethod函数:
1 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
2 List<Match> matches = new ArrayList<Match>();
3 List<T> directPathMatches = this.urlMap.get(lookupPath);
4 if (directPathMatches != null) {
5 addMatchingMappings(directPathMatches, matches, request);
6 }
7 if (matches.isEmpty()) {
8 // No choice but to go through all mappings...
9 addMatchingMappings(this.handlerMethods.keySet(), matches, request);
10 }
11
12 if (!matches.isEmpty()) {
13 Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
14 Collections.sort(matches, comparator);
15 if (logger.isTraceEnabled()) {
16 logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
17 }
18 Match bestMatch = matches.get(0);
19 if (matches.size() > 1) {
20 Match secondBestMatch = matches.get(1);
21 if (comparator.compare(bestMatch, secondBestMatch) == 0) {
22 Method m1 = bestMatch.handlerMethod.getMethod();
23 Method m2 = secondBestMatch.handlerMethod.getMethod();
24 throw new IllegalStateException(
25 "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
26 m1 + ", " + m2 + "}");
27 }
28 }
29 handleMatch(bestMatch.mapping, lookupPath, request);
30 return bestMatch.handlerMethod;
31 }
32 else {
33 return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
34 }
35 }
lookupHandlerMethod函数把获取的处理器包装成一个个Match,并对Match进行了排序(因为一个path可能不仅仅对应一个处理器,比如存在两个requestMap路径相同的函数,那么一个path就会对应两个处理器)之后取最匹配的那个。在这个过程中甚至还做了重复性检测,如果有两个一模一样的处理器存在,那说明我们的控制器写的有歧义了,直接抛出异常。我们看到第5行 addMatchingMappings(directPathMatches, matches, request);
1 private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
2 for (T mapping : mappings) {
3 T match = getMatchingMapping(mapping, request);
4 if (match != null) {
5 matches.add(new Match(match, this.handlerMethods.get(mapping)));
6 }
7 }
8 }
直接看到第5行 matches.add(new Match(match, this.handlerMethods.get(mapping)))
果然我们写的控制器方法对应的handler,都在这里头保存着。
接下来我们看看,这些handler是怎么保存到HM中的。我们在HM中找到了一个initHandlerMethods方法,从名字中就可以看出来这个方法是初始化methods用的,我们看看这个方法的内容:
1 protected void initHandlerMethods() {
2 if (logger.isDebugEnabled()) {
3 logger.debug("Looking for request mappings in application context: " + getApplicationContext());
4 }
5
6 String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
7 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
8 getApplicationContext().getBeanNamesForType(Object.class));
9
10 for (String beanName : beanNames) {
11 if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
12 isHandler(getApplicationContext().getType(beanName))){
13 detectHandlerMethods(beanName);
14 }
15 }
16 handlerMethodsInitialized(getHandlerMethods());
17 }
detectHandlerMethods(beanName)
1 protected void detectHandlerMethods(final Object handler) {
2 Class<?> handlerType =
3 (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
4
5 // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
6 final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
7 final Class<?> userType = ClassUtils.getUserClass(handlerType);
8
9 Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
10 @Override
11 public boolean matches(Method method) {
12 T mapping = getMappingForMethod(method, userType);
13 if (mapping != null) {
14 mappings.put(method, mapping);
15 return true;
16 }
17 else {
18 return false;
19 }
20 }
21 });
22
23 for (Method method : methods) {
24 registerHandlerMethod(handler, method, mappings.get(method));
25 }
26 }
看第24行,在这里注册HM所持的HandlerMethod,跟进去:
1 protected void registerHandlerMethod(Object handler, Method method, T mapping) {
2 HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
3 HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
4 if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
5 throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
6 "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +
7 oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
8 }
9
10 this.handlerMethods.put(mapping, newHandlerMethod);
11 if (logger.isInfoEnabled()) {
12 logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
13 }
14
15 Set<String> patterns = getMappingPathPatterns(mapping);
16 for (String pattern : patterns) {
17 if (!getPathMatcher().isPattern(pattern)) {
18 this.urlMap.add(pattern, mapping);
19 }
20 }
21 }
看第10行 this.handlerMethods.put(mapping, newHandlerMethod) ,一个个Put 进去了,这样,就完成了HandlerMethod的注册。在第18行 this.urlMap.add(pattern, mapping)
最后只要框架在启动是调用initMethod方法,就可以完成处理器的注册了。
自此,DispatcherServlet如何得到ModelAndView的过程就讲完了。