SpringMVC源码总结(⼀)HandlerMapping和HandlerAdapter⼊门
刚接触SpringMVC,对它的xml⽂件配置⼀直⽐较模模糊糊,最近花了⼀点时间稍微看了下源代码,再加上调试,开始逐渐理解它,⽹上的类似的内容有很多,写本⽂主要是⾃⼰加深⼀下理解。本⽂适合⽤过SpringMVC的开发者,⾔归正传,⾸先搭建⼀个最简单的⼯程体验⼀下。
该⼯程是基于maven的,pom配置不再说明,所使⽤的spring版本4.0.5。
⾸先是l⽂件配置,最简单的配置
Java代码
1. <!DOCTYPE web-app PUBLIC
2.  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
3.  "java.sun/dtd/web-app_2_3.dtd" >
4.
5. <web-app>
6.  <display-name>Archetype Created Web Application</display-name>
7.  <servlet>
8.        <servlet-name>mvc</servlet-name>
9.        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
10.        <load-on-startup>1</load-on-startup>
11.    </servlet>
12.
13.    <servlet-mapping>
14.        <servlet-name>mvc</servlet-name>
15.        <url-pattern>/*</url-pattern>
16.    </servlet-mapping>
17. </web-app>
然后是l⽂件的配置,上⾯配置DispatcherServlet会默认加载[servlet-name]-l⽂件。对于我的配置,会去加载l⽂件。
Java代码
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <beans xmlns="/schema/beans" xmlns:xsi="www.w
instance" xmlns:mvc="/schema/mvc" xmlns:util="/schema/util" xmlns:context="www.springframework.o
rg/schema/context"
3.    xsi:schemaLocation="/schema/beans
4.    /schema/beans/spring-beans-3.1.xsd
5.    /schema/mvc
6.    /schema/mvc/spring-mvc-3.1.xsd
7.    /schema/util
8.    /schema/util/spring-util-2.0.xsd
9.    /schema/context
10.    /schema/context/spring-context-3.2.xsd">
11.
12.    <bean name="/index" class="com.lg.mvc.HomeAction"></bean>
13.    <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
14.        <property name="templateLoaderPath" value="/WEB-INF/views" />
15.        <property name="defaultEncoding" value="utf-8" />
16.        <property name="freemarkerSettings">
17.            <props>
18.                <prop key="locale">zh_CN</prop>
19.            </props>
20.        </property>
21.    </bean>
22.    <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
23.        <property name="suffix" value=".html" />
24.        <property name="contentType" value="text/html;charset=utf-8" />
25.        <property name="requestContextAttribute" value="request" />
26.        <property name="exposeRequestAttributes" value="true" />
27.        <property name="exposeSessionAttributes" value="true" />
28.    </bean>
mvc的controller29. </beans>
在该配置中定义了⼀个HomeAction的Bean。内容为:
Java代码
1. package com.lg.mvc;
2.
3. import javax.servlet.http.HttpServletRequest;
4. import javax.servlet.http.HttpServletResponse;
5.
6. import org.springframework.web.servlet.ModelAndView;
7. import org.springframework.web.servlet.mvc.Controller;
8.
9. public class HomeAction implements Controller{
10.
11.    @Override
12.    public ModelAndView handleRequest(HttpServletRequest request,
13.            HttpServletResponse response) throws Exception {
14.        return new ModelAndView("hello");
15.    }
16. }
这是最原始的mvc做法,要继承Controller接⼝,先从原始的说起,最后再过渡到@Controller和@RequestMapping注解式的配置。它在l⽂件中的配置有⼀个关键的属性
name="/index"。
WEB-INF/view⽬录下有⼀个简单的hello.html,内容为:
Java代码
1. <html>
1. <html>
2.    <head>
3.
4.    </head>
5.    <body>
6.        hello lg !
7.    </body>
8. </html>
⾄此该⼯程就写完了,部署到tomcat中,项⽬路径为/,运⾏⼀下。
访问 localhost:8080/index
⾄此整个⼯程就算搭建成功了。
下⾯就要说说原理了。
⽤过python Django框架的都知道Django对于访问⽅式的配置就是,⼀个url路径和⼀个函数配对,你访问这个url,就会直接调⽤这个函数,简单明了。对于java的⾯向对象来说,就要分两步⾛。第⼀步⾸先要到是哪个对象,即handler,本⼯程的handler则是HomeAction对象。第⼆步要到访问的函数,即HomeAction的handleRequest⽅法。所以就出现了两个源码接⼝ HandlerMapping和HandlerAdapter,前者负责第⼀步,后者负责第⼆步。借⽤⽹上的SpringMVC架构图。
HandlerMapping接⼝的实现(只举了我认识的⼏个):
BeanNameUrlHandlerMapping :通过对⽐url和bean的name到对应的对象
SimpleUrlHandlerMapping :也是直接配置url和对应bean,⽐BeanNameUrlHandlerMapping功能更多
DefaultAnnotationHandlerMapping : 主要是针对注解配置@RequestMapping的,已过时
RequestMappingHandlerMapping :取代了上⾯⼀个
HandlerAdapter 接⼝实现:
HttpRequestHandlerAdapter :要求handler实现HttpRequestHandler接⼝,该接⼝的⽅法为                                                            void handleRequest(HttpServletRequest request, HttpServletResponse response)也就是  handler必须有⼀个handleRequest⽅法
SimpleControllerHandlerAdapter:要求handler实现Controller接⼝,该接⼝的⽅法为ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response),也就是本⼯程采⽤的
AnnotationMethodHandlerAdapter :和上⾯的DefaultAnnotationHandlerMapping配对使⽤的,也已过时
RequestMappingHandlerAdapter : 和上⾯的RequestMappingHandlerMapping配对使⽤,针对@RequestMapping
先简单的说下这个⼯程的流程,访问localhost:8080/index⾸先由DispatcherServlet进⾏转发,通过BeanNameUrlHandlerMapping(含有 /index->HomeAction的配置),到了HomeAction,然后再拿HomeAction和每个adapter进⾏适配,由于HomeAction实现了Controller接⼝,所以最终会有SimpleControllerHandlerAdapter来完成对HomeAction的handleRequest⽅法的调度。然后就顺利的执⾏了我们想要的⽅法,后⾯的内容不在本节中说明。
了解了⼤概流程,然后就需要看源代码了。
⾸先就是SpringMVC的⼊⼝类,DispatcherServlet,它实现了Servlet接⼝,不再详细说DispatcherServlet的细节,不然⼜是⼀⼤堆的内容。每次请求都会调⽤它的doService->doDispatch,我们关注的重点就在doDispatch⽅法中。
Java代码
1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exc
eption {
2.        HttpServletRequest processedRequest = request;
3.        HandlerExecutionChain mappedHandler = null;
4.        boolean multipartRequestParsed = false;
5.
6.        WebAsyncManager asyncManager = AsyncManager(request);
7.
8.        try {
9.            ModelAndView mv = null;
10.            Exception dispatchException = null;
11.
12.            try {
13.                processedRequest = checkMultipart(request);
14.                multipartRequestParsed = (processedRequest != request);
15.                      //这个是重点,第⼀步由HandlerMapping到对应的handler
16.                // Determine handler for the current request.
17.                mappedHandler = getHandler(processedRequest);
18.                if (mappedHandler == null || Handler() == null) {
19.                    noHandlerFound(processedRequest, response);
20.                    return;
21.                }
22.
23.                // Determine handler adapter for the current request.
23.                // Determine handler adapter for the current request.
24.                        //这是第⼆步,到合适的HandlerAdapter,然后由它来调度执⾏handler的⽅法
25.                HandlerAdapter ha = Handler());
26.
27.                // Process last-modified header, if supported by the handler.
28.                String method = Method();
29.                boolean isGet = "GET".equals(method);
30.                if (isGet || "HEAD".equals(method)) {
31.                    long lastModified = ha.getLastModified(request, Handler());
32.                    if (logger.isDebugEnabled()) {
33.                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
34.                    }
35.                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
36.                        return;
37.                    }
38.                }
39.
40.                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
41.                    return;
42.                }
43.
44.                try {
45.                    // Actually invoke the handler.
46.                    mv = ha.handle(processedRequest, response, Handler());
47.                }
48.                finally {
49.                    if (asyncManager.isConcurrentHandlingStarted()) {
50.                        return;
51.                    }
52.                }
53.
54.                applyDefaultViewName(request, mv);
55.                mappedHandler.applyPostHandle(processedRequest, response, mv);
56.            }
57.            catch (Exception ex) {
58.                dispatchException = ex;
59.            }
60.            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
61.        }
62.        catch (Exception ex) {
63.            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
64.        }
65.        catch (Error err) {
66.            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
67.        }
68.        finally {
69.            if (asyncManager.isConcurrentHandlingStarted()) {
70.                // Instead of postHandle and afterCompletion
71.                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
72.                return;
73.            }
74.            // Clean up any resources used by a multipart request.
75.            if (multipartRequestParsed) {
76.                cleanupMultipart(processedRequest);
77.            }
78.        }
79.    }
第⼀步详细查看:
Java代码
1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
2.        for (HandlerMapping hm : this.handlerMappings) {
3.            if (logger.isTraceEnabled()) {
4.                ace(
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.    }
可以看到就是通过遍历所有已注册的HandlerMapping来到对应的handler,然后构建出⼀个HandlerExecutionChain,它包含了handler和HandlerMapping本⾝的⼀些,如下Java代码
1. public class HandlerExecutionChain {
2.
3.    private final Object handler;
4.
5.    private HandlerInterceptor[] interceptors;
6.
7.    private List<HandlerInterceptor> interceptorList;
8.
9.        //其他代码省略
10. }
其中HandlerMapping的getHandler实现:
Java代码
1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
2.        Object handler = getHandlerInternal(request);
3.        if (handler == null) {
4.            handler = getDefaultHandler();
5.        }
6.        if (handler == null) {
7.            return null;
8.        }
9.        // Bean name or resolved handler?
10.        if (handler instanceof String) {
11.            String handlerName = (String) handler;
12.            handler = getApplicationContext().getBean(handlerName);
13.        }
14.        return getHandlerExecutionChain(handler, request);
15.    }
这⾥的getHandlerInternal(request)是个抽象⽅法,由具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,如果handler为String类型,则表⽰这个则会去Spring容器⾥⾯去这样名字的bean。
再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的具体实现(通过⼀系列的接⼝设计,之后再好好看看这个设计,到BeanNameUrlHandlerMapping这只⽤实现该⽅法中的⼀部分),如下
Java代码
1. public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
2.
3.    /**
4.      * Checks name and aliases of the given bean for URLs, starting with "/".
5.      */
6.    @Override
7.    protected String[] determineUrlsForHandler(String beanName) {
8.        List<String> urls = new ArrayList<String>();
9.        if (beanName.startsWith("/")) {
10.            urls.add(beanName);
11.        }
12.        String[] aliases = getApplicationContext().getAliases(beanName);
13.        for (String alias : aliases) {
14.            if (alias.startsWith("/")) {
15.                urls.add(alias);
16.            }
17.        }
18.        StringArray(urls);
19.    }
20.
21. }
这⾥⾯注释说,bean的name必须以/开头,它才处理,将信息存储在Map<String, Object> handlerMap中,对于本⼯程来说就是{'/index':HomeAction对象}。
⾄此这⾥完成了第⼀步,下⾯开始第⼆步,即⽅法HandlerAdapter ha = Handler());的具体实现:
Java代码
1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
2.        for (HandlerAdapter ha : this.handlerAdapters) {
3.            if (logger.isTraceEnabled()) {
4.                ace("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.    }
遍历所有的HandlerAdapter,判断他们是否⽀持这个handler。
我们来看下HttpRequestHandlerAdapter的supports(handler)⽅法:
Java代码
1. public class HttpRequestHandlerAdapter implements HandlerAdapter {
2.
3.    @Override
4.    public boolean supports(Object handler) {
5.          //就是判断handler是否实现了HttpRequestHandler接⼝
6.        return (handler instanceof HttpRequestHandler);
7.    }
8.
9.    @Override
10.    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
11.            throws Exception {
12.            //若handler实现了HttpRequestHandler接⼝,则调⽤该接⼝的⽅法,执⾏我们在该⽅法中写的业务逻辑
13.        ((HttpRequestHandler) handler).handleRequest(request, response);
14.        return null;
15.    }
16.
17.    @Override
18.    public long getLastModified(HttpServletRequest request, Object handler) {
19.        if (handler instanceof LastModified) {
19.        if (handler instanceof LastModified) {
20.            return ((LastModified) handler).getLastModified(request);
21.        }
22.        return -1L;
23.    }
24.
25. }
同理SimpleControllerHandlerAdapter也是这样类似的逻辑
Java代码
1. public class SimpleControllerHandlerAdapter implements HandlerAdapter {
2.
3.    @Override
4.    public boolean supports(Object handler) {
5.        return (handler instanceof Controller);
6.    }
7.
8.    @Override
9.    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
10.            throws Exception {
11.
12.        return ((Controller) handler).handleRequest(request, response);
13.    }
14.
15.    @Override
16.    public long getLastModified(HttpServletRequest request, Object handler) {
17.        if (handler instanceof LastModified) {
18.            return ((LastModified) handler).getLastModified(request);
19.        }
20.        return -1L;
21.    }
22.
23. }
剩余两个AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就⽐较复杂,我也没看。
按照本⼯程的配置,则SimpleControllerHandlerAdapter是⽀持HomeAction的,然后就会执⾏SimpleControllerHandlerAdapter的handle(processedRequest, response, Handler())
⽅法。本质上就会调⽤HomeAction实现Controller接⼝的⽅法。⾄此就分析完了。
了解过程了之后,然后就是最重要的也是经常配置出问题的地⽅。DispatcherServlet的handlerMappings和handlerAdapters的来源问题。
DispatcherServlet初始化的时候,会调⽤⼀个⽅法如下:
Java代码
1. protected void initStrategies(ApplicationContext context) {
2.        initMultipartResolver(context);
3.        initLocaleResolver(context);
4.        initThemeResolver(context);
5. //初始化⼀些HandlerMapping
6.        initHandlerMappings(context);
7. //初始化⼀些HandlerAdapter
8.        initHandlerAdapters(context);
9.        initHandlerExceptionResolvers(context);
10.        initRequestToViewNameTranslator(context);
11.        initViewResolvers(context);
12.        initFlashMapManager(context);
13.    }
这⾥可以看到,它会初始化⼀些HandlerMapping和HandlerAdapter,这两个⽅法⾮常重要,理解了这两个⽅法你就会知道,配置不对问题出在哪⾥,下⾯具体看下这两个⽅法:
Java代码
1. private void initHandlerMappings(ApplicationContext context) {
2.        this.handlerMappings = null;
3.
4.        if (this.detectAllHandlerMappings) {
5.            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
6.            Map<String, HandlerMapping> matchingBeans =
7.                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
8.            if (!matchingBeans.isEmpty()) {
9.                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
10.                // We keep HandlerMappings in sorted order.
11.                OrderComparator.sort(this.handlerMappings);
12.            }
13.        }
14.        else {
15.            try {
16.                HandlerMapping hm = Bean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
17.                this.handlerMappings = Collections.singletonList(hm);
18.            }
19.            catch (NoSuchBeanDefinitionException ex) {
20.                // Ignore, we'll add a default HandlerMapping later.
21.            }
22.        }
23.
24.        // Ensure we have at least one HandlerMapping, by registering
25.        // a default HandlerMapping if no other mappings are found.
26.        if (this.handlerMappings == null) {
27.            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
28.            if (logger.isDebugEnabled()) {

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。