zoukankan      html  css  js  c++  java
  • SpringMVC深入理解

    核心类与接口

    - DispatcherServlet 前置控制器
    - HandlerMapping 请求映射(到Controller)
    - HandlerAdapter 请求映射(到Controller类的方法上)
    - Controller 控制器
    - HandlerIntercepter 拦截器
    - ViewResolver 视图映射
    - View 视图处理

    启动过程


    Spring MVC启动过程大致分为两个过程:
    - ContextLoaderListener初始化,读取context-param中的contextConfigLocation指定的配置文件,创建ROOT Context,通过调用继承自ContextLoader的initWebApplicationContext方法实例化Spring IoC容器,并将此容器实例注册到ServletContext中
    - ContextLoaderListener初始化完毕后,开始初始化web.xml中配置的DispatcherServlet,DispatcherServlet的初始化又包括了视图管理器、异常处理器、映射管理等等;

     1    /**
     2      * Initialize the strategy objects that this servlet uses.
     3      * <p>May be overridden in subclasses in order to initialize further strategy objects.
     4      */
     5     protected void initStrategies(ApplicationContext context) {
     6         initMultipartResolver(context);
     7         initLocaleResolver(context);
     8         initThemeResolver(context);
     9         initHandlerMappings(context);
    10         initHandlerAdapters(context);
    11         initHandlerExceptionResolvers(context);
    12         initRequestToViewNameTranslator(context);
    13         initViewResolvers(context);
    14         initFlashMapManager(context);
    15     }


    ContextLoaderListener初始化的是 WebApplicationContext, 创建后可以从ServletContext中获取,WebApplicationContext是应用程序内共享的,最多只有一个,如果寻求简单也可以不初始化此容器。与之不同 DispatcherServlet可以有多个,并共享一个WebApplicationContext容器,每一个DispatcherServlet有不同的配置,控制不同的WEB访问。一般将 DAO、Service 层Bean共享的放在ContextLoaderListener配置的容器中,将WEB层的Bean放在特定的DispatcherServlet配置的容器中。

    SpringMVC利用Spring的注入特性初始化资源文件,只需要调用setPropertyValues方法就可将contextConfigLocation属性设置到对应实例中,也就是以依赖注入的方式初始化属性。

    时序图如下(盗图):




    请求处理流程

    官网上的图



    涉及到核心类与接口的过程描述:

    客户端浏览器发送http请求,被`DispatcherServlet`捕获,调用关键的doDispatch方法,遍历所有注册为`Controller`的bean,为请求寻找关联映射,其中遍历查找的函数getHandler和getHandlerAdapter的源码:

     1 /**
     2      * Return the HandlerExecutionChain for this request.
     3      * <p>Tries all handler mappings in order.
     4      * @param request current HTTP request
     5      * @return the HandlerExecutionChain, or {@code null} if no handler could be found
     6      */
     7     protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
     8         for (HandlerMapping hm : this.handlerMappings) {
     9             if (logger.isTraceEnabled()) {
    10                 logger.trace(
    11                         "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
    12             }
    13             HandlerExecutionChain handler = hm.getHandler(request);
    14             if (handler != null) {
    15                 return handler;
    16             }
    17         }
    18         return null;
    19     }
    20     
    21     
    22     /**
    23      * Return the HandlerAdapter for this handler object.
    24      * @param handler the handler object to find an adapter for
    25      * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
    26      */
    27     protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    28         for (HandlerAdapter ha : this.handlerAdapters) {
    29             if (logger.isTraceEnabled()) {
    30                 logger.trace("Testing handler adapter [" + ha + "]");
    31             }
    32             if (ha.supports(handler)) {
    33                 return ha;
    34             }
    35         }
    36         throw new ServletException("No adapter for handler [" + handler +
    37                 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    38     }



    找到匹配的映射后`HandlerAdapter`会依次调用preHandle、handle(返回ModelAndView)、postHandle方法,所有步骤完成后调用processDispatchResult函数处理结果,并返回View给客户端。postDispatchResult函数和其中调用的render函数源码如下:

     1 /**
     2      * Handle the result of handler selection and handler invocation, which is
     3      * either a ModelAndView or an Exception to be resolved to a ModelAndView.
     4      */
     5     private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
     6             HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
     7 
     8         boolean errorView = false;
     9 
    10         if (exception != null) {
    11             if (exception instanceof ModelAndViewDefiningException) {
    12                 logger.debug("ModelAndViewDefiningException encountered", exception);
    13                 mv = ((ModelAndViewDefiningException) exception).getModelAndView();
    14             }
    15             else {
    16                 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
    17                 mv = processHandlerException(request, response, handler, exception);
    18                 errorView = (mv != null);
    19             }
    20         }
    21 
    22         // Did the handler return a view to render?
    23         if (mv != null && !mv.wasCleared()) {
    24             render(mv, request, response);
    25             if (errorView) {
    26                 WebUtils.clearErrorRequestAttributes(request);
    27             }
    28         }
    29         else {
    30             if (logger.isDebugEnabled()) {
    31                 logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
    32                         "': assuming HandlerAdapter completed request handling");
    33             }
    34         }
    35 
    36         if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    37             // Concurrent handling started during a forward
    38             return;
    39         }
    40 
    41         if (mappedHandler != null) {
    42             mappedHandler.triggerAfterCompletion(request, response, null);
    43         }
    44     }
    45 
    46 
    47     /**
    48      * Render the given ModelAndView.
    49      * <p>This is the last stage in handling a request. It may involve resolving the view by name.
    50      * @param mv the ModelAndView to render
    51      * @param request current HTTP servlet request
    52      * @param response current HTTP servlet response
    53      * @throws ServletException if view is missing or cannot be resolved
    54      * @throws Exception if there's a problem rendering the view
    55      */
    56     protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    57         // Determine locale for request and apply it to the response.
    58         Locale locale = this.localeResolver.resolveLocale(request);
    59         response.setLocale(locale);
    60 
    61         View view;
    62         if (mv.isReference()) {
    63             // We need to resolve the view name.
    64             view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
    65             if (view == null) {
    66                 throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
    67                         "' in servlet with name '" + getServletName() + "'");
    68             }
    69         }
    70         else {
    71             // No need to lookup: the ModelAndView object contains the actual View object.
    72             view = mv.getView();
    73             if (view == null) {
    74                 throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
    75                         "View object in servlet with name '" + getServletName() + "'");
    76             }
    77         }
    78 
    79         // Delegate to the View object for rendering.
    80         if (logger.isDebugEnabled()) {
    81             logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
    82         }
    83         try {
    84             if (mv.getStatus() != null) {
    85                 response.setStatus(mv.getStatus().value());
    86             }
    87             view.render(mv.getModelInternal(), request, response);
    88         }
    89         catch (Exception ex) {
    90             if (logger.isDebugEnabled()) {
    91                 logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
    92                         getServletName() + "'", ex);
    93             }
    94             throw ex;
    95         }
    96     }


    这就是一个完整的处理http请求的过程。盗图一张:

    时序图如下(来源:http://neoremind.com/2016/02/springmvc%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B8%B8%E7%94%A8%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/):



    配置实例


    这里放的是最简单的配置,可以通过这个简单的配置实例回顾一下上面的过程。

    目录结构

        -SpringMVCDemo
            -src
                -me.cyan
                    -WelcomeController
            -web
                -WEB-INF
                    -applicationContext.xml
                    -dispatcher-servlet.xml
                    -web.xml
                -index.jsp
            -pom.xml
            
    pom.xml
    引入的包

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     4     <modelVersion>4.0.0</modelVersion>
     5     <groupId>me.cyan</groupId>
     6     <artifactId>SpringMVCDemo</artifactId>
     7     <name>SpringMVCDemo</name>
     8     <packaging>war</packaging>
     9     <version>1.0.0</version>
    10 
    11     <properties>
    12         <spring-version>4.2.6.RELEASE</spring-version>
    13     </properties>
    14 
    15     <dependencies>
    16         <dependency>
    17             <groupId>org.springframework</groupId>
    18             <artifactId>spring-context</artifactId>
    19             <version>${spring-version}</version>
    20         </dependency>
    21         <dependency>
    22             <groupId>org.springframework</groupId>
    23             <artifactId>spring-core</artifactId>
    24             <version>${spring-version}</version>
    25         </dependency>
    26         <dependency>
    27             <groupId>org.springframework</groupId>
    28             <artifactId>spring-web</artifactId>
    29             <version>${spring-version}</version>
    30         </dependency>
    31         <dependency>
    32             <groupId>org.springframework</groupId>
    33             <artifactId>spring-beans</artifactId>
    34             <version>${spring-version}</version>
    35         </dependency>
    36         <dependency>
    37             <groupId>commons-logging</groupId>
    38             <artifactId>commons-logging</artifactId>
    39             <version>1.2</version>
    40         </dependency>
    41         <dependency>
    42             <groupId>org.springframework</groupId>
    43             <artifactId>spring-webmvc</artifactId>
    44             <version>${spring-version}</version>
    45         </dependency>
    46         <dependency>
    47             <groupId>org.springframework</groupId>
    48             <artifactId>spring-aop</artifactId>
    49             <version>${spring-version}</version>
    50         </dependency>
    51         <dependency>
    52             <groupId>org.springframework</groupId>
    53             <artifactId>spring-expression</artifactId>
    54             <version>${spring-version}</version>
    55         </dependency>
    56     </dependencies>
    57 </project>


    web.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
     3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
     5          version="3.1">
     6          
     7     <!--配置文件路径-->
     8     <context-param>
     9         <param-name>contextConfigLocation</param-name>
    10         <param-value>/WEB-INF/applicationContext.xml</param-value>
    11     </context-param>
    12     <listener>
    13         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    14     </listener>
    15     
    16     <!--SpringMVC核心servlet-->
    17     <servlet>
    18         <servlet-name>dispatcher</servlet-name>
    19         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    20         <load-on-startup>1</load-on-startup>
    21     </servlet>
    22     <servlet-mapping>
    23         <servlet-name>dispatcher</servlet-name>
    24         <url-pattern>/</url-pattern>
    25     </servlet-mapping>
    26 </web-app>



    dispatcher-servlet.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
     4        xmlns:context="http://www.springframework.org/schema/context"
     5        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
     6 
     7     <!-- 默认的注解映射的支持 -->
     8     <mvc:annotation-driven />
     9 
    10     <!-- 自动扫描的包名 -->
    11     <context:component-scan base-package="me.cyan" />
    12 
    13 </beans>



    WelcomeController

     1 package me.cyan;
     2 
     3 import org.springframework.stereotype.Controller;
     4 import org.springframework.web.bind.annotation.RequestMapping;
     5 import org.springframework.web.bind.annotation.ResponseBody;
     6 
     7 /**
     8  * Created by cyan on 16/5/23.
     9  */
    10 
    11 @Controller
    12 public class welcomeController {
    13 
    14     @RequestMapping("/hello")
    15     public @ResponseBody String sayhello(){
    16         return "hello Spring MVC!";
    17     }
    18 }

    运行结果


  • 相关阅读:
    浅尝DesignPattern_AbstractFactory
    浅尝DesignPattern_OCP&DIP
    浅尝DesignPattern_Strategy
    浅尝EffectiveCSharp_2
    浅尝EffectiveCSharp_5
    浅尝EffectiveCSharp_3
    浅尝DesignPattern_Factory
    浅尝DesignPattern_Template
    我的ASP.NET之旅_基础知识&安装运行环境
    浅尝DesignPattern_Proxy
  • 原文地址:https://www.cnblogs.com/verlen11/p/5521536.html
Copyright © 2011-2022 走看看