Spring源码解析-springmvc

springmvc是spring为web应用程序提供的一个模块。
Springmvc是基于servlet实现的,通过实现Servlet接口的DispatcherServlet来封装核心功能,通过请求分派给处理程序。
在分析源码之前先来一套简单的spirngmvc程序。
首先我们需要配置web.xml文件,服务器启动后的工作就从这开始。

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!--spring配置文件的位置-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <!--上下文加载器,在ServletContext初始化后载入配置文件-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--进行请求调度的servlet,在DispatcherServlet载入后会根据servlet-name的值加载xml文件,也可以自己定义,加入<init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>-->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--在这里我们自己定义配置文件的位置-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>
</web-app>

②创建接收参数的model

public class User {
    private String userName;
    private Integer age;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

③编写控制器

public class UserController extends AbstractController{
    protected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        List<User> userList = new ArrayList<User>();
        User user = new User();
        user.setUserName("小明");
        user.setAge(14);
        User user1 = new User();
        user1.setUserName("大明");
        user1.setAge(15);
        userList.add(user);
        userList.add(user1);
        //转发到名为userList的jsp
        return new ModelAndView("userList","users",userList);
    }
}

④编写jsp文件

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2017/9/16
  Time: 0:52
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <c:forEach items="${users}" var="user">
        <c:out value="${user.userName}"/><br/>
        <c:out value="${user.age}"/><br/>
    </c:forEach>
</body>
</html>

⑤编写springmvc的配置文件,也就是之前的springmvc.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/task
        http://www.springframework.org/schema/task/spring-task.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
    <bean id="userController" class="com.creat.controller.UserController"/>
    <!--编写一个简单的url映射器来找到控制器-->
    <bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <!--请求路径,以及对应的控制器映射-->
                <prop key="/userList.htm">userController</prop>
            </props>
        </property>
    </bean>
      <!--配置视图解析器,在ModelAndView返回的视图名上加上指定的prefix前缀和suffix后缀-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

⑥启动服务器,访问userList
在访问后有个小问题,c标签失效了,后来查了资料,发现:由于Jsp2.0向后兼容的特性, 当遇到使用Jsp 1.2(Servlet v2.3)的网站时会默认的禁用JSP2.0 EL,所以导致c:out不能正确输出。所以加入在jsp文件中加入<%@ page isELIgnored=”false”%>,最后得到了正确结果
这里写图片描述

源码分析

我们从web.xml配置文件中可以看出,当服务器启动时,ServletContext进行初始化时,ContextLoaderListener就会被加载,然后我们进入这个类。

/*ContextLoaderListener实现了ServletContextListener这个监听器,所以当ServletContext进行初始化时,这个类会被加载,且调用contextInitialized方法,ServletContext会在服务器启动时被加载初始化,ServletContext相当于web应用的一个全局变量,一个web应用只有一个ServletContext*/
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }
    //当ServletContext初始化时调用这个方法
    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }
    //当ServletContext被销毁时时调用这个方法
    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}

①ContextLoaderListener的核心就是初始化WebApplicationContext,将其放入ServletContext
我们进入this.initWebApplicationContext(event.getServletContext());这个方法查看

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    /*如果WebApplicationContext在ServletContext中已经存在,那么就抛出异常*/
    if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
            throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
        } else {
            /*日志输出*/
            Log logger = LogFactory.getLog(ContextLoader.class);
            servletContext.log("Initializing Spring root WebApplicationContext");
            if(logger.isInfoEnabled()) {
                logger.info("Root WebApplicationContext: initialization started");
            }

            long startTime = System.currentTimeMillis();

            try {
                if(this.context == null) {
                    /*创建并初始化WebApplicationContext*/
                    this.context = this.createWebApplicationContext(servletContext);
                }

进入这个createWebApplicationContext方法

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        /*确定容器的class类*/
        Class<?> contextClass = this.determineContextClass(sc);

继续进入这个determineContextClass方法

protected Class<?> determineContextClass(ServletContext servletContext) {
        /*这个值可以在web.xml配置是设置
        <context-param>
    <param-name>contextClass</param-name>
    <paramvalue>
   org.springframework.web.context.WebApplicationContext
   </param-value>
  </context-param>实现*/
        String contextClassName = servletContext.getInitParameter("contextClass");
        /*如果不为空*/
        if(contextClassName != null) {
            try {
                /*得到该class名对应的实际Class类*/
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            } catch (ClassNotFoundException var4) {
                throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
            }
        } else {
            /*如果为空,那么从defaultStrategies中获取*/
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            } catch (ClassNotFoundException var5) {
                throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
            }
        }
    }

那么defaultStrategies中是如何得到的contextClass的类名,
我们在静态代码块中找到了这个defaultStrategies的过程,

static {
        try {
            /*获取ContextLoader.properties配置文件,从配置文件中获取需要被加载的context类名*/
            ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException var1) {
            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + var1.getMessage());
        }
        currentContextPerThread = new ConcurrentHashMap(1);
    }

回到之前的创建WebApplicatonContext的方法

        if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        } else {
            /*实例化WebApplicationContext类*/
            return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
        }
    }

回到之前初始化context的方法

                /*判断这个context是否是ConfigurableWebApplicationContext的对象,默认的context是XmlWebApplcationContext,实现了ConfigurableWebApplicationContext接口,所以正确*/,
                if(this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                    if(!cwac.isActive()) {
                        if(cwac.getParent() == null) {
                            ApplicationContext parent = this.loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }

             //配置和刷新上下文,也是在这里加载的配置文件           this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }

我们进入这个configureAndRefreshWebApplicationContext方法查看

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        String configLocationParam;
        if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
            configLocationParam = sc.getInitParameter("contextId");
            if(configLocationParam != null) {
                wac.setId(configLocationParam);
            } else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }

        wac.setServletContext(sc);
        /*提取之前<context-param>设置的contextConfigLocation的值*/
        configLocationParam = sc.getInitParameter("contextConfigLocation");
        //填充到容器属性中
        if(configLocationParam != null) {
            wac.setConfigLocation(configLocationParam);
        }

        ConfigurableEnvironment env = wac.getEnvironment();
        if(env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
        }

        this.customizeContext(sc, wac);
        /*刷新,也就是在这里做的对xml配置文件的加载和bean的注册,可以看之前的博客《Spring源码解析-容器功能扩展》那一章*/
        wac.refresh();
    }

继续回到之前的方法


  /*将已经实例化的WebApplicationContext加入servletContext中*/              servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
                  /*得到类加载器*/
                ClassLoader ccl = Thread.currentThread().getContextClassLoader();
                 /*如果两个类加载器相同*/
                if(ccl == ContextLoader.class.getClassLoader()) {
                    currentContext = this.context;
                } else if(ccl != null) {
                    /*如果得到的类加载器不为空,那么加入map中*/
                    currentContextPerThread.put(ccl, this.context);
                }

                if(logger.isDebugEnabled()) {
                    logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
                }

                if(logger.isInfoEnabled()) {
                    long elapsedTime = System.currentTimeMillis() - startTime;
                    logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
                }

                return this.context;
            } catch (RuntimeException var8) {
                /*省略一堆异常处理*/
            }
        }
    }

这就是WebApplicationContext的初始化,主要做了三件事:1、创建WebApplicationContext实例;2、加载spring的配置文件;3、将创建的实例加入到ServletContext中。
②现在来分析DispatcherServlet,DispatcherServlet实现了Servlet的接口,Servlet是用来处理处理客户端请求的一个服务者。
servlet的生命周期有三个:
(1)初始化
- servlet容器加载sevlet类
- servlet容器根据web.xml创建ServletConfig对象,该对象包含了对servlet初始化的配置信息
- servlet容器创建一个servlet对象,在创建时将ServletConfig装入对象中
- servlet容器调用servlet对象的init方法进行初始化,加载ServletConfig的配置以及其他一些操作
(2)运行阶段
servlet容器针对客户端的请求,创建servletrequest,调用对应的servlet处理,得到servletresponse对象,返回给客户端。
(3)销毁
web应用终止时,servlet容器会先调用servlet对象的destory方法,然后销毁,同时释放资源;
③那么我们来看一下DispatcherServlet这个类,首先根据生命周期servlet容器会调init方法,然而我们并没有在这里类里面找到,那么我们去父类里面找。我们在父类HttpServletBean中找到了这个方法。

public final void init() throws ServletException {
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
        }

        try {
            //解析init-param,验证,然后封装到PropertyValues中
            PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
            /*将这个对象转换成BeanWrapper*/
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            /*将servletcontext包装成资源加载器*/
            ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
            /*注册自定义属性编辑器,遇到Resource类型就用resoureditor解析*/
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
            /*留给子类实现*/
            this.initBeanWrapper(bw);
            /*将init-param的所有属性进行注入,其实就是注入到servlet中*/
            bw.setPropertyValues(pvs, true);
        } catch (BeansException var4) {
            if(this.logger.isErrorEnabled()) {
                this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
            }

            throw var4;
        }
        /*子类FrameworkServlet实现了这个方法*/
        this.initServletBean();
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
        }

    }

④接着我们找到FrameworkServlet的initServletBean方法

protected final void initServletBean() throws ServletException {
        this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
        if(this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
        }

        long startTime = System.currentTimeMillis();

        try {
            //得到初始化的webApplicationContext,这个webApplicationContext跟之前监听器加载的context不一样,这主要作为springmvc的容器,那个是普通的spring容器
            this.webApplicationContext = this.initWebApplicationContext();
            /*让子类覆盖*/
            this.initFrameworkServlet();
        } catch (ServletException var5) {
           /*省略异常处理*/
        }

       /*省略日志打印*/
        }

    }

⑤接着我们来看initWebApplicationContext方法

protected WebApplicationContext initWebApplicationContext() {
        /*从servletContext中获取WebApplicationContext,也就是在context-param中配置的容器*/
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;
        /*如果this.webApplicationContext存在,不过按照这种web.xml配置文件的方式来看是不会存在的,这个servlet是tomcat进行实例化的,这个this.webApplicationContext是在构造函数中传入的,tomcat肯定不会再实例化对象时传入一个容器对象,一般是我们在取消web.xml配置,进行手动初始化时传入的。例如下面这段代码:
        public class WebInitializer implements WebApplicationInitializer{

    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext rootContext =
                new AnnotationConfigWebApplicationContext();
        rootContext.register(ServiceConfig.class, AopConfig.class);
        rootContext.setServletContext(servletContext);
        servletContext.addListener(new ContextLoaderListener(rootContext));

        AnnotationConfigWebApplicationContext context =
                new AnnotationConfigWebApplicationContext();
        context.register(ContollerConfig.class);
        context.setServletContext(servletContext);

        ServletRegistration.Dynamic servlet =  servletContext.addServlet("dispatcher",new DispatcherServlet(context));
        servlet.addMapping("/");
        servlet.setLoadOnStartup(1);
    }
}*/
        if(this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            //如果是这个ConfigurableWebApplicationContext类型的对象
            if(wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
                //而且还存在
                if(!cwac.isActive()) {
                    if(cwac.getParent() == null) {
                        //将在监听器中创建的容器设置为父容器
                        cwac.setParent(rootContext);
                    }
                   /*刷新上下文,也就是在这里加载spring的xml配置文件*/ this.configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if(wac == null) {
            /*根据contextAttribute属性加载context*/
            wac = this.findWebApplicationContext();
        }

进入findWebApplicationContext方法查看

protected WebApplicationContext findWebApplicationContext() {
        /*如果web.xml中servlet配置了参数contextAtrribute,那么就可以得到*/
        String attrName = this.getContextAttribute();
        if(attrName == null) {
            return null;
        } else {
            /*如果存在,从servletcontext中根据这个atrrName键获取WebApplicationContext,因为我们之前在ContextLoaderListener加载时就已经把WebApplicationContext放入了servletcontext中,不过一般不进行配置attrName是不存在的*/
            WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext(), attrName);
            if(wac == null) {
                throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
            } else {
                return wac;
            }
        }
    }

返回之前的方法

        //wac不存在
        if(wac == null) {
            //那么进行创建WebApplicationContext
            wac = this.createWebApplicationContext(rootContext);
        }

进入createWebApplicationContext方法

protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
        return this.createWebApplicationContext((ApplicationContext)parent);
    }
    继续进入
    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    /*获取servlet初始化参数contextClass,如果没有设置默认为XmlWebApplicationContext.class*/
        Class<?> contextClass = this.getContextClass();
        if(this.logger.isDebugEnabled()) {
        //略          if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            //略
        } else {
            //反射方式实例化
            ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
            wac.setEnvironment(this.getEnvironment());
            //设置父容器
            wac.setParent(parent);
        /*设置spring位置文件的路径,也就是在servlet初始参数中配置的contextConfigLocation*/            wac.setConfigLocation(this.getContextConfigLocation());
    /*配置和刷新容器,也就是在这将springmvc的配置文件加载到容器中并注册bean*/            this.configureAndRefreshWebApplicationContext(wac);
            return wac;
        }
    }

返回之前的初始化容器方法

        if(!this.refreshEventReceived) {
            this.onRefresh(wac);
        }

        if(this.publishContext) {
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);

        }

        return wac;
    }

上面的onfresh方法其实是加载一些springmvc的组件。
我们进入这个方法查看,该方法由子类DispatcherServlet实现

protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }
    /*我们从这个英文方法就能看出对上面组件进行初始化,初始化的过程无非是先从容器中拿到bean对象,以及一些初始化设置*/
protected void initStrategies(ApplicationContext context) {
        this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
    }

到这为止,spring和springmvc的配置文件已经加载完毕了。
接下来就是要看如何springmvc对请求的处理。
我们知道tomcat服务器收到一个http请求,会去调用servlet的service方法,然后service方法再去调用doPost、doGet等方法处理。我们进入FrameworkServlet中查看service方法。
我们可以看到,如果http请求类型不是PATCH那么交给父类的service方法处理,如果是,那么就交给processRequst处理。

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if(HttpMethod.PATCH != httpMethod && httpMethod != null) {
            super.service(request, response);
        } else {
            this.processRequest(request, response);
        }

    }

接着进入service方法,我们可以看到对请求的处理,根据不同的请求方法类型,交给不同的方法处理,我们最常用的还是doPost和doGet

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if(method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if(lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if(ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if(method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if(method.equals("POST")) {
            this.doPost(req, resp);
        } else if(method.equals("PUT")) {
            this.doPut(req, resp);
        } else if(method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if(method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if(method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }

我们查看doGet和doPost方法,可见最后都是交给了processRequest方法处理

protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response);
    }

    protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response);
    }

然后我们进入这个processRequest方法。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        //提取线程原来的LocalContext
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        //创建一个新的LocalContext
        LocaleContext localeContext = this.buildLocaleContext(request);
        //提取线程原来的RequestAttributes
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        //创建新的RequestAttributes
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
        //将创建的localecontext和RequestAttributes和request绑定到当前线程
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
            //调用doService处理请求
            this.doService(request, response);
        } catch (ServletException var17) {
            failureCause = var17;
            throw var17;
        } catch (IOException var18) {
            failureCause = var18;
            throw var18;
        } catch (Throwable var19) {
            failureCause = var19;
            throw new NestedServletException("Request processing failed", var19);
        } finally {
            //恢复线程之前的状态
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if(requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
            /*省略日志输出*/
            //发布事件通知
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }

可以看出请求的具体处理是在doService中实现的,该方法由子类DispatcherServlet实现,查看这个方法

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        /*省略日志输出*/

        Map<String, Object> attributesSnapshot = null;
        if(WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            //得到所有属性的名字
            Enumeration attrNames = request.getAttributeNames();

            label108:
            while(true) {
                String attrName;
                do {
                    if(!attrNames.hasMoreElements()) {
                        break label108;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
                //将request中的属性装入attributesSnapshot
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }

/*将容器装入request属性中*/        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
/*将国际化的解析器装入request属性中*/
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        /*将主题的解析器装入request属性中*/
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        /*将主题的资源装入request属性中*/
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.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方法处理
            this.doDispatch(request, response);
        } finally {
            if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

    }

可见doService并没有处理请求,只是做了一些预处理,然后叫交给了doDispatch方法,我们进入这个方法查看

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

                try {
                    /*如果是MultipartContext类型就将request转换成MultipartHttpServletRequest*/
                    processedRequest = this.checkMultipart(request);
                    /*看processedRequest有没有变,变了就说明是Multipart类型*/
                    multipartRequestParsed = processedRequest != request;
                    /*得到处理器链*/
                    mappedHandler = this.getHandler(processedRequest);

进入这个getHandler方法查看

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Iterator var2 = this.handlerMappings.iterator();
        //遍历所有加载的HandlerMapping
        HandlerExecutionChain handler;
        do {
            if(!var2.hasNext()) {
                return null;
            }

            HandlerMapping hm = (HandlerMapping)var2.next();
            if(this.logger.isTraceEnabled()) {
                this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
            }
            //从HandlerMapping中取得Handler
            handler = hm.getHandler(request);
        } while(handler == null);

        return handler;
    }

HandlerMapping是个接口,我们需要从他的实现类中去找getHandler方法,例如我们经常用的RequestMappingHandlerMapping这个类,他的这个方法是在AbstractHandlerMapping这个父类中实现的

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        /*根据request获取handler,主要是直接匹配和通配符匹配,然后将Handler封装成HandlerExecutionChain类型,在这匹配到的是一个对应的执行方法,所以这个方法的执行还是采用了反射机制,将这个handler封装成HandlerExecutionChain的目的主要是为了后期可以添加拦截器*/
        Object handler = this.getHandlerInternal(request);
        /*如果没找到那就使用默认的handler*/
        if(handler == null) {
            handler = this.getDefaultHandler();
        }

        if(handler == null) {
            return null;
        } else {
            //如果handler不是HandlerExecutionChain,那就是string类型,那就从容器中找到这个handler
            if(handler instanceof String) {
                String handlerName = (String)handler;
                handler = this.getApplicationContext().getBean(handlerName);
            }
            /*这个方法就是为了将匹配的拦截器添加到执行器链中*/
            HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
            if(CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
                CorsConfiguration config = globalConfig != null?globalConfig.combine(handlerConfig):handlerConfig;
                executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
            }

            return executionChain;
        }
    }

得到处理器链后回到之前的doDispatch方法

                    /*如果没有找到对应的handler*/
                    if(mappedHandler == null || mappedHandler.getHandler() == null) {
    /*处理没找到信息*/                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                    /*找到对应的适配器*/
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    /*获取方法类型*/
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    /*如果是get或者head*/
                    if(isGet || "HEAD".equals(method)) {
                        /*获取到请求的last-modidfied信息*/
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if(this.logger.isDebugEnabled()) {
                            this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        }
                        /*如果确认该请求的东西没有被修改,那么就直接返回,这个是http协议中的last-modified缓存机制,在客户端第一次请求该url时,服务器会在响应头中加上last-modified响应头,客户端在第二次请求该url时会向服务器发送请求头“if-modified-since”,请求头,如果服务器端内容没有变,那么返回http304状态码,减少了网络带宽*/
                        if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

              /*调用拦截器方法*/      if(!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                    /*真正的调用对应请求的方法,然后返回视图*/
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

同样,这个HandlerAdapter是个接口,我们需要找到它的实现类,在这里我们用的是RequestMappingHandlerAdapter,然后再父类中找到了handle方法

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return this.handleInternal(request, response, (HandlerMethod)handler);
    }
    /*进入这个方法,这个方法由子类实现*/
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        this.checkRequest(request);
        ModelAndView mav;
        if(this.synchronizeOnSession) {
            /*以不创建session的方式获取session,也就是说session是在getSession(true)时创建的,如果没有session就会创建,有的话就直接返回,参数为false的话,有的话直接返回,没有也不会创建session*/
            HttpSession session = request.getSession(false);

            if(session != null) {
                /*如果获取到session,那么就获取到这个session中的互斥锁*/
                Object mutex = WebUtils.getSessionMutex(session);
                /*因为这个是运行在多线程环境下的作业,session又是共享域,所有需要对session加锁*/
                synchronized(mutex) {
                    /*执行handler的方法*/
                    mav = this.invokeHandlerMethod(request, response, handlerMethod);
                }
            } else {
                /*如果没有session,那么就不会对共享变量进行访问,所以不用加锁*/
                mav = this.invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            mav = this.invokeHandlerMethod(request, response, handlerMethod);
        }
        /*处理Cache-Control缓存头*/
        if(!response.containsHeader("Cache-Control")) {
            if(this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            } else {
                this.prepareResponse(response);
            }
        }
        //返回视图
        return mav;
    }    

来看一下控制器中方法的调用,也就是invokeHandlerMethod方法

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        /*包装成ServletWebRequest对象*/
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        Object result;
        try {
            /*得到参数绑定工厂*/
            WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
            /*Handler方法包装成ServletInvocableHandlerMethod */
            ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
/*设置方法参数解析器*/            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
/*配置方法返回值解析器,这个返回值可能会被解析成json数据,或者视图模型等等*/            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()) {
                result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                if(this.logger.isDebugEnabled()) {
                    this.logger.debug("Found concurrent result value [" + result + "]");
                }

                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }
            /*执行方法*/
            invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
            if(!asyncManager.isConcurrentHandlingStarted()) {
                ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
                return var15;
            }

            result = null;
        } finally {
            webRequest.requestCompleted();
        }

        return (ModelAndView)result;
    }

返回 doDispatch方法

                                  if(asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
              this.applyDefaultViewName(processedRequest, mv);
                  /*应用拦截器的后置处理器*/  mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

          /*对结果进行处理,包括异常的处理,以及视图的处理,异常的处理主要是调用在配置文件中定义的异常处理bean的异常处理方法,模型视图的处理主要是一些跳转,转发等问题*/      this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
/*完成处理激活触发器*/                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if(asyncManager.isConcurrentHandlingStarted()) {
                if(mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if(multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

总结

DispatcherServlet这个类首先会在调用init初始化时加载springmvc的配置文件,然后将配置中配置的组件进行加载。
接着就是处理请求的功能,主要分为以下几步:
①获取要请求对应的处理器链,这里处理器链里面主要包括了拦截器和最终调用的方法;
②找到对应的处理适配器;
③执行预处理拦截方法;
④通过处理适配器调用最终执行的方法;
⑤执行后处理拦截方法;
⑥对统一抛出的异常进行处理;
⑦对返回的视图进行处理;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值