SpringMVC源码解析

Spring版本是5.1.x

代码示例

image.png
web.xml
web.xml是用来初始化配置信息的,即为Context配置监听器,过滤器以及Servlet。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
  
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  
  <servlet>
    <!--名称 -->
    <servlet-name>springmvc</servlet-name>
    <!-- Servlet类 -->
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--SpringMVC配置参数文件的位置 -->
      <param-name>contextConfigLocation</param-name>
      <!--默认名称为ServletName-servlet.xml -->
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <!-- 启动顺序,数字越小,启动越早 -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <!--所有请求都会被springmvc拦截 -->
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  
</web-app>

applicationContext.xml
applicationContext.xml是作为spring的配置文件,用于创建spring容器,配置了采用注解方式以及扫描的路径,这里主要是扫描Service和Dao

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-4.0.xsd">
  
  <context:annotation-config />
  <context:component-scan base-package="com.huangsz.springmvcdemo">
  </context:component-scan>
  
</beans>

spring-mvc.xml
applicationContext.xml是作为springmvc的配置文件,用于创建springmvc子容器,配置了采用注解方式,扫描的路径,这里主要是扫描Controller,还有视图解析器。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:mvc="http://www.springframework.org/schema/mvc"
	   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-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

	<!-- scan the package and the sub package -->
	<context:component-scan base-package="com.huangsz.springmvcdemo"/>

	<!-- don't handle the static resource -->
	<mvc:default-servlet-handler />

	<!-- if you use annotation you must configure following setting -->
	<mvc:annotation-driven />

	<!-- configure the InternalResourceViewResolver -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		  id="internalResourceViewResolver">
		<!-- 前缀 -->
		<property name="prefix" value="/WEB-INF/jsp/" />
		<!-- 后缀 -->
		<property name="suffix" value=".jsp" />
	</bean>
</beans>

TestController

package com.huangsz.springmvcdemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author: hsz
 * @date: 2021/3/18 18:12
 * @description:
 */

@Controller
@RequestMapping("/test")
public class TestController {

    @Autowired
    private TestService testService;

    @RequestMapping("/print")
    public String print(@RequestParam("p1") String p1, @RequestParam("p2") int p2) {
        testService.test();
        System.out.println("p1=" + p1 + ", p2=" + p2);
        return "test";
    }
}

TestService

package com.huangsz.springmvcdemo;

import org.springframework.stereotype.Service;

/**
 * @author: hsz
 * @date: 2022/3/7 14:13
 * @description:
 */

@Service
public class TestService {

	public void test() {
		System.out.println("Hello World");
	}
}

test.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
  <html>
    <head>
      <title>Title</title>
    </head>
    <body>
      Hello World
    </body>
</html>

启动Tomcat,访问http://localhost:8080/test/print?p1=1&p2=2
image.png
image.png

源码解析

创建Spring容器并刷新

在解析Tomcat源码一章中,启动StandardContext时会去加载web.xml并解析,然后会实例化应用配置的监听器,即这里web.xml中配置的ContextLoaderListener。

下面是Tomcat启动StandardContext的代码:

protected synchronized void startInternal() throws LifecycleException {
    
    // 省略。。。
    
    try {
        if (ok) {
            
            // 当前状态为STARTING_PREP,发布CONFIGURE_START_EVENT事件,去解析web.xml,完善Context,构建Wrapper组件
            fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
            
            // 启动context下的wrapper组件,其实这里已经启动过了,在添加到context时会启动
            for (Container child : findChildren()) {
                if (!child.getState().isAvailable()) {
                    child.start();
                }
            }
        }
        
        // 实例化应用配置的监听器
        if (ok) {
            if (!listenerStart()) {
                log.error(sm.getString("standardContext.listenerFail"));
                ok = false;
            }
        }
        
        try {
            // 启动session管理器
            Manager manager = getManager();
            if (manager instanceof Lifecycle) {
                ((Lifecycle) manager).start();
            }
        } catch(Exception e) {
            log.error(sm.getString("standardContext.managerFail"), e);
            ok = false;
        }
        
        // 实例化应用配置的过滤器
        if (ok) {
            if (!filterStart()) {
                log.error(sm.getString("standardContext.filterFail"));
                ok = false;
            }
        }
    } finally {
        // Unbinding thread
        unbindThread(oldCCL);
    }
}

这里面会调用listenerStart方法来初始化监听器

public boolean listenerStart() {
    
    // 省略
    
    // 获取全部的应用监听器
    Object instances[] = getApplicationLifecycleListeners();
    if (instances == null || instances.length == 0) {
        return ok;
    }
    
    ServletContextEvent event = new ServletContextEvent(getServletContext());
    ServletContextEvent tldEvent = null;
    if (noPluggabilityListeners.size() > 0) {
        noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
        tldEvent = new ServletContextEvent(noPluggabilityServletContext);
    }
    for (int i = 0; i < instances.length; i++) {
        // 如果不是ServletContextListener类型的监听器则跳过
        if (!(instances[i] instanceof ServletContextListener))
            continue;
        ServletContextListener listener =
            (ServletContextListener) instances[i];
        try {
            fireContainerEvent("beforeContextInitialized", listener);
            if (noPluggabilityListeners.contains(listener)) {
                listener.contextInitialized(tldEvent);
            } else {
                listener.contextInitialized(event);
            }
            fireContainerEvent("afterContextInitialized", listener);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            fireContainerEvent("afterContextInitialized", listener);
            getLogger().error
                (sm.getString("standardContext.listenerStart",
                              instances[i].getClass().getName()), t);
            ok = false;
        }
    }
    return ok;
    
  }

ContextLoaderListener刚好实现了ServletContextListener接口
image.png

ContextLoaderListener的作用就是启动Web容器时,读取在contextConfigLocation中定义的xml文件(一般存放spring的相关配置),自动装配ApplicationContext的配置信息,并产生WebApplicationContext对象,然后将这个对象放置在ServletContext的属性里,这样我们只要得到Servlet就可以得到WebApplicationContext对象,并利用这个对象访问spring容器管理的bean。
摘自:https://blog.csdn.net/shang_0122/article/details/119334625

所以会去调ContextLoaderListener的contextInitialized方法

@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}

在这里会去初始化Spring容器(父容器)并刷新,该操作由它的父类ContextLoader完成

public WebApplicationContext initWebApplicationContext(ServletContext 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!");
    }

    servletContext.log("Initializing Spring root WebApplicationContext");
    Log logger = LogFactory.getLog(ContextLoader.class);
    if (logger.isInfoEnabled()) {
        logger.info("Root WebApplicationContext: initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        // 如果是采用注解方式的,这里的context就不为空,
        // 用xml方式的,这里context为空,需要创建
        if (this.context == null) {
            // 创建spring容器
            // 根据“ contextClass ”和“ contextConfigLocation ”上下文参数创建
            this.context = createWebApplicationContext(servletContext);
        }
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent ->
                    // determine parent for root web application context, if any.
                    // 设置父容器,spring容器一般没有父容器
                    ApplicationContext parent = loadParentContext(servletContext);
                    cwac.setParent(parent);
                }
                // 刷新spring容器
                configureAndRefreshWebApplicationContext(cwac, 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) {
            currentContextPerThread.put(ccl, this.context);
        }

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

        return this.context;
    }
    catch (RuntimeException | Error ex) {
        logger.error("Context initialization failed", ex);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
        throw ex;
    }
}

创建spring容器

首先会创建spring容器

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    // 得到容器的类型
    Class<?> contextClass = determineContextClass(sc);
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                                              "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }
    return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

protected Class<?> determineContextClass(ServletContext servletContext) {
    // 获取web.xml中配置的contextClass参数
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    if (contextClassName != null) {
        try {
            // 根据类名获取Class类型
            return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                    "Failed to load custom context class [" + contextClassName + "]", ex);
        }
    }
    else {
        // 如果没有配置,则使用默认的类型
        contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); // 默认是WebApplicationContext
        try {
            // 根据类名获取Class类型
            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                    "Failed to load default context class [" + contextClassName + "]", ex);
        }
    }
}

会先判断web.xml中是否配置了contextClass参数,如果配了容器类型,则实例化该类。如果没有配置,则使用默认的类型,从defaultStrategies中获取WebApplicationContext对应的属性值。defaultStrategies中的属性是从ContextLoader类所在的类路径中的ContextLoader.properties文件加载,而该文件中配置了一个默认的WebApplicationContext。

private static final Properties defaultStrategies;

static {
    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    try {
        // 从ContextLoader类所在的类路径中的ContextLoader.properties文件加载
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
    }
}

image.png
所以默认spring容器是 XmlWebApplicationContext。

然后通过(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass)实例化一个容器出来

public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
    Assert.notNull(clazz, "Class must not be null");
    if (clazz.isInterface()) {
        throw new BeanInstantiationException(clazz, "Specified class is an interface");
    }
    try {
        // 通过构造方法实例化
        return instantiateClass(clazz.getDeclaredConstructor());
    }
    catch (NoSuchMethodException ex) {
        Constructor<T> ctor = findPrimaryConstructor(clazz);
        if (ctor != null) {
            return instantiateClass(ctor);
        }
        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
    }
    catch (LinkageError err) {
        throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
    }
}

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
    Assert.notNull(ctor, "Constructor must not be null");
    try {
        ReflectionUtils.makeAccessible(ctor);
        return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
                KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
    }
    catch (InstantiationException ex) {
        throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
    }
    catch (IllegalAccessException ex) {
        throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
    }
    catch (IllegalArgumentException ex) {
        throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
    }
    catch (InvocationTargetException ex) {
        throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
    }
}

从下面这个截图也可以看出创建出来的容器是叫根容器
image.png

刷新spring容器

然后就是配置spring容器并刷新了

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        // The application context id is still set to its original default value
        // -> assign a more useful id based on available information
        String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
        if (idParam != null) {
            wac.setId(idParam);
        }
        else {
            // Generate default id...
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }

    // 为容器设置 ServletContext
    wac.setServletContext(sc);
    // 获取web.xml中设置的contextConfigLocation参数,即spring的配置文件,
    // 后面需要根据该配置文件中的配置刷新spring容器
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    if (configLocationParam != null) {
        wac.setConfigLocation(configLocationParam);
    }

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    }

    // 在刷新之前初始化容器,可在刷新之前先对容器进行处理
    customizeContext(sc, wac);
    // 最终刷新容器
    wac.refresh();
}

这里在最终刷新spring容器之前,会先初始化Spring ConfigurableApplicationContext的回调接口,调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法。

protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
    // 获得ApplicationContextInitializer列表
    List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
            determineContextInitializerClasses(sc);

    for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
        Class<?> initializerContextClass =
                GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
        if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
            throw new ApplicationContextException(String.format(
                    "Could not apply context initializer [%s] since its generic parameter [%s] " +
                    "is not assignable from the type of application context used by this " +
                    "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
                    wac.getClass().getName()));
        }
        this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
    }

    // 可以排序
    AnnotationAwareOrderComparator.sort(this.contextInitializers);
    // 调用每个ApplicationContextInitializer实现类的initialize,
    // 并将ConfigurableApplicationContext 作为参数传入
    for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
        initializer.initialize(wac);
    }
}

可以在web.xml中配置contextInitializerClasses参数来配置自定义的ApplicationContextInitializer

<context-param>
  <param-name>contextInitializerClasses</param-name>
  <param-value>com.huangsz.springmvcdemo.CustomApplicationContextInitializer</param-value>
</context-param>

该接口典型的应用场景是web应用中需要编程方式对应用上下文做初始化。比如,注册属性源(property sources)或者针对上下文的环境信息environment激活相应的profile。一般是留给外部扩展的,springboot就有几个类实现了该接口。

最后刷新spring容器,调用了AbstractApplicationContext类的refresh方法,refresh方法的解析可以看Spring IOC 源码解析之refresh(二)

下面是spring容器刷新的调用栈,可以看到就是从tomcat启动StandardContext为入口,在初始化监听器时创建并刷新spring容器的。
image.png

创建SpringMVC容器并刷新

创建springmvc容器

接下来就该创建springmvc容器了,那这个容器是在哪里创建的呢?
其实也可以通过跟上面一样的调用栈来看
image.png
可以看到也是在启动StandardContext里,在调用loadOnstartup方法里。
还记得在web.xml中我们为DispatcherServlet配置了一个load-on-startup

<servlet>
  <!--名称 -->
  <servlet-name>springmvc</servlet-name>
  <!-- Servlet类 -->
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <!--SpringMVC配置参数文件的位置 -->
    <param-name>contextConfigLocation</param-name>
    <!--默认名称为ServletName-servlet.xml -->
    <param-value>classpath:spring-mvc.xml</param-value>
  </init-param>
  <!-- 启动顺序,数字越小,启动越早 -->
  <load-on-startup>1</load-on-startup>
</servlet>

当值为0或者大于0时,表示在应用启动时就加载这个servlet。
我们通过Tomcat的StandardContext里的代码来看下

if (ok) {
    if (!listenerStart()) {
        log.error(sm.getString("standardContext.listenerFail"));
        ok = false;
    }
}

// Check constraints for uncovered HTTP methods
// Needs to be after SCIs and listeners as they may programmatically
// change constraints
if (ok) {
    checkConstraintsForUncoveredMethods(findConstraints());
}

try {
    // Start manager
    Manager manager = getManager();
    if (manager instanceof Lifecycle) {
        ((Lifecycle) manager).start();
    }
} catch(Exception e) {
    log.error(sm.getString("standardContext.managerFail"), e);
    ok = false;
}

// Configure and call application filters
if (ok) {
    if (!filterStart()) {
        log.error(sm.getString("standardContext.filterFail"));
        ok = false;
    }
}

// Load and initialize all "load on startup" servlets
if (ok) {
    if (!loadOnStartup(findChildren())){
        log.error(sm.getString("standardContext.servletFail"));
        ok = false;
    }
}

可以看到,在listenerStart下面的不远处,就有一处loadOnStartup,看注释也清楚这里是加载并初始化所有配置了“load on startup”的servlet。

public boolean loadOnStartup(Container children[]) {

    // 使用TreeMap来存放需要在启动时就加载的servlet,根据loadOnStartup排序
    TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
    for (int i = 0; i < children.length; i++) {
        Wrapper wrapper = (Wrapper) children[i];
        int loadOnStartup = wrapper.getLoadOnStartup();
        // 这里如果loadOnStartup >= 0,就会将该servlet存放到map中
        if (loadOnStartup < 0)
            continue;
        Integer key = Integer.valueOf(loadOnStartup);
        ArrayList<Wrapper> list = map.get(key);
        if (list == null) {
            list = new ArrayList<>();
            map.put(key, list);
        }
        list.add(wrapper);
    }

    // Load the collected "load on startup" servlets
    for (ArrayList<Wrapper> list : map.values()) {
        for (Wrapper wrapper : list) {
            try {
                // 加载servlet
                wrapper.load();
            } catch (ServletException e) {
                getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
                      getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
                // NOTE: load errors (including a servlet that throws
                // UnavailableException from the init() method) are NOT
                // fatal to application startup
                // unless failCtxIfServletStartFails="true" is specified
                if(getComputedFailCtxIfServletStartFails()) {
                    return false;
                }
            }
        }
    }
    return true;

}

加载servlet,在这里就是DispatcherServlet

public synchronized void load() throws ServletException {
    instance = loadServlet();

    // 。。。
}
public synchronized Servlet loadServlet() throws ServletException {

    // 如果已经有一个该servlet实例,则直接返回该实例
    if (!singleThreadModel && (instance != null))
        return instance;

    PrintStream out = System.out;
    if (swallowOutput) {
        SystemLogHandler.startCapture();
    }

    Servlet servlet;
    try {
        long t1=System.currentTimeMillis();
        // Complain if no servlet class has been specified
        if (servletClass == null) {
            unavailable(null);
            throw new ServletException
                (sm.getString("standardWrapper.notClass", getName()));
        }

        InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
        try {
            // 实例化该servlet
            servlet = (Servlet) instanceManager.newInstance(servletClass);
        } catch (ClassCastException e) {
            unavailable(null);
            // Restore the context ClassLoader
            throw new ServletException
                (sm.getString("standardWrapper.notServlet", servletClass), e);
        } catch (Throwable e) {
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            unavailable(null);

            // Added extra log statement for Bugzilla 36630:
            // https://bz.apache.org/bugzilla/show_bug.cgi?id=36630
            if(log.isDebugEnabled()) {
                log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
            }

            // Restore the context ClassLoader
            throw new ServletException
                (sm.getString("standardWrapper.instantiate", servletClass), e);
        }

        // 。。。

        // 初始化servlet
        initServlet(servlet);

        fireContainerEvent("load", this);

        loadTime=System.currentTimeMillis() -t1;
    } finally {
        if (swallowOutput) {
            String log = SystemLogHandler.stopCapture();
            if (log != null && log.length() > 0) {
                if (getServletContext() != null) {
                    getServletContext().log(log);
                } else {
                    out.println(log);
                }
            }
        }
    }
    return servlet;

}

初始化DispatcherServlet

private synchronized void initServlet(Servlet servlet)
        throws ServletException {

    if (instanceInitialized && !singleThreadModel) return;

    // Call the initialization method of this servlet
    try {
        if( Globals.IS_SECURITY_ENABLED) {
            boolean success = false;
            try {
                Object[] args = new Object[] { facade };
                SecurityUtil.doAsPrivilege("init",
                                           servlet,
                                           classType,
                                           args);
                success = true;
            } finally {
                if (!success) {
                    // destroy() will not be called, thus clear the reference now
                    SecurityUtil.remove(servlet);
                }
            }
        } else {
            // 初始化
            servlet.init(facade);
        }

        instanceInitialized = true;
    } catch (UnavailableException f) {
        unavailable(f);
        throw f;
    } catch (ServletException f) {
        // If the servlet wanted to be unavailable it would have
        // said so, so do not call unavailable(null).
        throw f;
    } catch (Throwable f) {
        ExceptionUtils.handleThrowable(f);
        getServletContext().log(sm.getString("standardWrapper.initException", getName()), f);
        // If the servlet wanted to be unavailable it would have
        // said so, so do not call unavailable(null).
        throw new ServletException
            (sm.getString("standardWrapper.initException", getName()), f);
    }
}
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}
public final void init() throws ServletException {

    // 获取在web.xml配置的初始化参数<init-param>,并将其设置到DispatcherServlet中
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }

    // 初始化
    initServletBean();
}

这里主要就是设置在web.xml中配置的contextConfigLocation属性,此属性指定SpringMVC的配置文件地址。然后调用FrameworkServlet的initServletBean方法初始化,在这里面会进行springmvc容器的创建和刷新。

protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
    if (logger.isInfoEnabled()) {
        logger.info("Initializing Servlet '" + getServletName() + "'");
    }
    long startTime = System.currentTimeMillis();

    try {
        // 创建springmvc容器并刷新
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (logger.isDebugEnabled()) {
        String value = this.enableLoggingRequestDetails ?
                "shown which may lead to unsafe logging of potentially sensitive data" :
                "masked to prevent unsafe logging of potentially sensitive data";
        logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                "': request parameters and headers will be " + value);
    }

    if (logger.isInfoEnabled()) {
        logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
    }
}

// 初始化并发布此 servlet 的 WebApplicationContext。委托createWebApplicationContext实际创建上下文
protected WebApplicationContext initWebApplicationContext() {
    // 获取根容器,即spring容器
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    
    // 如果容器已经存在了,则刷新
    if (this.webApplicationContext != null) {
        // A context instance was injected at construction time -> use it
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent -> set
                    // the root application context (if any; may be null) as the parent
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        // 在ServletContext中寻找是否有Spring MVC容器,初次运行是没有的,
        // Spring MVC初始化完毕ServletContext就有了Spring MVC容器
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        // 没有则创建一个,并将根容器传进去,这里就是spring容器作为根容器
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        // Either the context is not a ConfigurableApplicationContext with refresh
        // support or the context injected at construction time had already been
        // refreshed -> trigger initial onRefresh manually here.
        synchronized (this.onRefreshMonitor) {
            onRefresh(wac);
        }
    }

    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}

protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
    return createWebApplicationContext((ApplicationContext) parent);
}

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    // 获取容器类型,默认也是XmlWebApplicationContext
    Class<?> contextClass = getContextClass();
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException(
                "Fatal initialization error in servlet with name '" + getServletName() +
                "': custom WebApplicationContext class [" + contextClass.getName() +
                "] is not of type ConfigurableWebApplicationContext");
    }
    // 实例化springmvc容器
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    // 将spring容器设置为父容器
    wac.setParent(parent);
    // 获取web.xml中配置的springmvc配置文件地址
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    // 配置并刷新容器
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

刷新springmvc容器

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        // The application context id is still set to its original default value
        // -> assign a more useful id based on available information
        if (this.contextId != null) {
            wac.setId(this.contextId);
        }
        else {
            // Generate default id...
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
        }
    }

    wac.setServletContext(getServletContext());
    wac.setServletConfig(getServletConfig());
    wac.setNamespace(getNamespace());
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    }

    // 这里是留给子类重写,也是可以对容器进行设置修改
    postProcessWebApplicationContext(wac);
    applyInitializers(wac);
    // 刷新容器
    wac.refresh();
}

最后刷新springmvc容器,也是调用了AbstractApplicationContext类的refresh方法。

总结下就是下面这张图
在这里插入图片描述

处理@RequestMapping注解,建立url和方法的关系

入口在RequestMappingHandlerMapping类中,这是一个处理器映射器,在实例化后进行初始化时调用afterPropertiesSet方法,来完成url和controller的映射。

@Override
public void afterPropertiesSet() {
    this.config = new RequestMappingInfo.BuilderConfiguration();
    this.config.setUrlPathHelper(getUrlPathHelper());
    this.config.setPathMatcher(getPathMatcher());
    this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
    this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
    this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
    this.config.setContentNegotiationManager(getContentNegotiationManager());

    super.afterPropertiesSet();
}

前面代码不管,直接看调用父类AbstractHandlerMethodMapping的afterPropertiesSet方法

public void afterPropertiesSet() {
    initHandlerMethods();
}

protected void initHandlerMethods() {
    // 获取容器中所有的bean
    for (String beanName : getCandidateBeanNames()) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            // 处理bean
            processCandidateBean(beanName);
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

需要先获取到所有的bean

protected String[] getCandidateBeanNames() {
    // detectHandlerMethodsInAncestorContexts 表示是否从子容器以及root容器中获取,
    // 这里默认是false,表示只从子容器获取,因为通过前面对容器的解析,controller相关的bean就是在子容器中
    return (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
            obtainApplicationContext().getBeanNamesForType(Object.class));
}

只要bean名称前缀不是“scopedTarget.”就进一步处理

protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;
    try {
        // 获取bean的类型
        beanType = obtainApplicationContext().getType(beanName);
    }
    catch (Throwable ex) {
        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
        if (logger.isTraceEnabled()) {
            logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
        }
    }
    // 判断是不是处理器,通过判断类上是否有@Controller或@RequestMapping注解
    if (beanType != null && isHandler(beanType)) {
        detectHandlerMethods(beanName);
    }
}

protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

protected void detectHandlerMethods(Object handler) {
    Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        // 确保是被代理的类,即原始的类,而不是代理类
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 解析controller类,将Method与RequestMappingInfo对应
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                    try {
                        return getMappingForMethod(method, userType);
                    }
                    catch (Throwable ex) {
                        throw new IllegalStateException("Invalid mapping on handler class [" +
                                userType.getName() + "]: " + method, ex);
                    }
                });
        if (logger.isTraceEnabled()) {
            logger.trace(formatMappings(userType, methods));
        }
        methods.forEach((method, mapping) -> {
            // 获得可调用的方法实例,即被Aop代理包装后的方法实例,因为最终调的是代理类,如果有的话
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            // 进行注册
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

先解析controller类,建立Method与RequestMappingInfo的对应关系。

RequestMappingInfo是解析@RequestMapping得到的一个类,可以认为就是@RequestMapping,包含请求路径,请求方法,请求头等信息。
遍历每个方法,根据@RequestMapping注解创建对应的RequestMappingInfo

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 根据方法上的@RequestMapping创建RequestMappingInfo
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // 根据类上的@RequestMapping创建RequestMappingInfo
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            // 将类上的映射信息和方法上的映射信息结合起来
            info = typeInfo.combine(info);
        }
        // 如果有前缀,再加上前缀
        String prefix = getPathPrefix(handlerType);
        if (prefix != null) {
            info = RequestMappingInfo.paths(prefix).build().combine(info);
        }
    }
    return info;
}

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    // 获取注解
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);// 获取方法上的@RequestMapping注解
    RequestCondition<?> condition = (element instanceof Class ?
            getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
    // 有这个注解就创建RequestMappingInfo,没有则返回null
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

建立完所有的Method和RequestMappingInfo的对应关系后,然后遍历这个map开始注册

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    this.mappingRegistry.register(mapping, handler, method);
}

委托给内部类MappingRegistry处理

// mapping就是RequestMappingInfo,handler就是Controller实例名称,method方法实例
public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        // 先创建HandlerMethod实例,保存controller中方法的信息,比如,所属类,方法实例,参数列表等
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        // 验证是否已经注册过相同的映射,因为映射应该是唯一的
        assertUniqueMethodMapping(handlerMethod, mapping);
        // 建立 RequestMappingInfo 与 HandlerMethod 的映射
        this.mappingLookup.put(mapping, handlerMethod);

        // 建立 url 与 RequestMappingInfo 的映射,因为一个接口可能有多个路径,
        // 所以这里用list,可能会映射多次
        // 比如这样的:@RequestMapping(value = {"/print1", "/print2"})
        List<String> directUrls = getDirectUrls(mapping);
        for (String url : directUrls) {
            this.urlLookup.add(url, mapping);
        }

        String name = null;
        if (getNamingStrategy() != null) {
            name = getNamingStrategy().getName(handlerMethod, mapping);
            addMappingName(name, handlerMethod);
        }

        // 这里跟跨域相关的,可以不管
        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            this.corsLookup.put(handlerMethod, corsConfig);
        }

        // 将所有信息封装为MappingRegistration然后与RequestMappingInfo关联,后续就可以通过url获取到相关的所有映射信息
        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
    }
    finally {
        this.readWriteLock.writeLock().unlock();
    }
}

这里会将方法信息封装为HandlerMethod,包含所属类,方法实例,参数列表等,然后建立RequestMappingInfo 与 HandlerMethod 的映射。
image.png
还会建立 url 与 RequestMappingInfo 的映射
在这里插入图片描述
最后将RequestMappingInfo与MappingRegistration关联,MappingRegistration中封装了RequestMappingInfo、HandlerMethod、url以及name。

这样就完成了url到controller中方法的映射了。一个请求过来,就可以根据请求url从urlLookup中找出对应的RequestMappingInfo ,再根据RequestMappingInfo 从mappingLookup中找出对应的HandlerMethod,根据HandlerMethod中的方法实例反射调用即可。

初始化DispacherServlet

在真正启动完成之前,还需要为DispacherServlet实例初始化
image.png
可以看到目前DispacherServlet里的这些组件还都是null的,需要为这些组件赋值后该DispacherServlet才可以使用。那在什么时候初始化的呢?
image.png
DispacherServlet中有一个onRefresh方法,调用了initStrategies方法,看方法名就知道是初始化方法
image.png
通过在initStrategies的调用处打一个断点,debug下
image.png
可以看出在springmvc容器完成刷新后,会发布完成刷新的事件,监听器监听到该事件后,会调用到DispacherServlet的onRefresh方法,再调用initStrategies方法。

protected void initStrategies(ApplicationContext context) {
    // 初始化文件上传处理器
    initMultipartResolver(context);
    // 初始化国际化配置
    initLocaleResolver(context);
    // 初始化主题处理器
    initThemeResolver(context);
    // 初始化处理器映射器,用于处理url到controller的映射
    initHandlerMappings(context);
    // 初始化处理器适配器,用来调用处理器方法
    initHandlerAdapters(context);
    // 初始化异常处理器
    initHandlerExceptionResolvers(context);

    initRequestToViewNameTranslator(context);
    // 初始化视图解析器
    initViewResolvers(context);
    initFlashMapManager(context);
}

这里主要看下initHandlerMappings,因为这几个方法逻辑都差不多

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    // 是否要从容器中获取所有的HandlerMapping实现类,默认是true
    // web.xml:
    // <init-pararn>
    //     <pararn-narne>detectAllHandlerMappings</pararn-narne>
    //     <pararn-value>false</pararn-value>
    // </init-pararn>
    if (this.detectAllHandlerMappings) {
        // 查找 ApplicationContext 中的所有 HandlerMapping,包括祖先上下文
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            // 赋值给handlerMappings
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // 按优先级排序
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }
    else {
        try {
            // 只从容器中获取beanName为handlerMapping的HandlerMapping
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerMapping later.
        }
    }

    // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
    // 如果没有名为handlerMapping的HandlerMapping,则使用一个默认的,
    // 使用“DispatcherServlet.properties”文件(与 DispatcherServlet 类位于同一包中)来确定类名
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

DispacherServlet处理请求

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 {
            // 检查是否有文件上传
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // 根据当前请求获得执行链
            mappedHandler = getHandler(processedRequest);
            // 没找到处理器的处理
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // 根据HandlerMethod确定当前请求的处理器适配器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            // 执行拦截器的preHandle方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 调用controller,处理请求,返回视图
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

            applyDefaultViewName(processedRequest, mv);
            // 执行拦截器的postHandle方法
            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);
            }
        }
    }
}

首先是文件上传的处理,这个不是我们的重点,所以跳过,然后根据当前请求获得执行链。

根据当前请求获得执行链

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        // 根据request获取HandlerExecutionChain,一般是由RequestMappingHandlerMapping来获取
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

这里需要通过处理器映射器来生成一条执行链,handlerMappings默认有3个
image.png
由于我们是使用注解@RequestMapping,所以这里使用的是RequestMappingHandlerMapping

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 由具体子类完成,获取到HandlerMethod
    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 = obtainApplicationContext().getBean(handlerName);
    }

    // 获取到执行链,即封装了处理器和拦截器
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

    if (logger.isTraceEnabled()) {
        logger.trace("Mapped to " + handler);
    }
    else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
        logger.debug("Mapped to " + executionChain.getHandler());
    }

    if (CorsUtils.isCorsRequest(request)) {// 跨域处理
        CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }

    return executionChain;
}
/**
 * 查找给定请求的处理器方法
 */
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 获取request中的url
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    this.mappingRegistry.acquireReadLock();
    try {
        // 根据url查询HandlerMethod
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        // 如果handlerMethod中的bean只是名称而不是controller实例,则要根据名称获取到实例
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    // 从urlLookup根据url找到匹配的RequestMappingInfo,
    // 因为可能一个url对应多个RequestMappingInfo,所以这里用list
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
        // 如果有匹配的,则检查其它属性是否符合要求,比如请求方法,请求参数,请求头等
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        // 没有匹配的则只能所有的RequestMappingInfo都拿去重新匹配下
        addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    }

    // 匹配到了,则选一个最佳匹配
    if (!matches.isEmpty()) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        matches.sort(comparator);
        Match bestMatch = matches.get(0);
        if (matches.size() > 1) {
            if (logger.isTraceEnabled()) {
                logger.trace(matches.size() + " matching mappings: " + matches);
            }
            if (CorsUtils.isPreFlightRequest(request)) {
                return PREFLIGHT_AMBIGUOUS_MATCH;
            }
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                String uri = request.getRequestURI();
                throw new IllegalStateException(
                        "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
            }
        }
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
        handleMatch(bestMatch.mapping, lookupPath, request);
        // 返回最佳匹配的handlerMethod
        return bestMatch.handlerMethod;
    }
    // 还没有匹配的,那就说明真没有一个方法可以处理该请求
    else {
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    for (T mapping : mappings) {
        // 获取匹配的RequestMappingInfo
        // 检查给定的 RequestMappingInfo 是否与当前请求匹配,比如请求类型,请求参数等是否匹配,
        // 并返回一个(可能是新的)实例
        // match不为空时,就说明当前请求与给定的 RequestMappingInfo匹配
        T match = getMatchingMapping(mapping, request);
        if (match != null) {
            // 根据RequestMappingInfo从mappingLookup中取出对应的HandlerMethod,
            // 然后将新的RequestMappingInfo实例和HandlerMethod封装成Match
            matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
        }
    }
}

获取到处理器后,即需要再获取到拦截器,然后封装成一个执行链

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) {
        // 如果是MappedInterceptor类型的拦截器,需要判断下是否跟请求路径匹配
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
            // 不是MappedInterceptor类型的拦截器直接添加到执行链中
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

确定请求的处理器适配器

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

在这里handler就是HandlerMethod,handlerAdapters是如下3个
image.png
循环遍历每个适配器,调用supports方法判断是否适用于handler
HttpRequestHandlerAdapter

@Override
public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
}

SimpleControllerHandlerAdapter

@Override
public boolean supports(Object handler) {
    return (handler instanceof Controller);
}

HandlerMethod没有实现HttpRequestHandler接口和Controller接口,所以都返回false。
RequestMappingHandlerAdapter

@Override
public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
    return true;
}

明显返回true,所以请求的处理器适配器是RequestMappingHandlerAdapter。

反射调用处理请求的方法,返回ModelAndView

在调controller的方法之前,还需要执行拦截器的preHandle方法。

然后调用适配器的handle方法

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    return handleInternal(request, response, (HandlerMethod) handler);
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    // 验证请求
    checkRequest(request);

    // Execute invokeHandlerMethod in synchronized block if required.
    // 默认false
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No HttpSession available -> no mutex necessary
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    }
    else {
        // 调用
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            prepareResponse(response);
        }
    }

    return mav;
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // 从给定的HandlerMethod定义创建一个ServletInvocableHandlerMethod,
        // ServletInvocableHandlerMethod继承自HandlerMethod,扩展了功能,
        // 可以设置参数解析器和返回值处理器
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            // 设置参数解析器
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            // 设置返回值处理器
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        // 用来记录model和view
        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()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            LogFormatUtils.traceDebug(logger, traceOn -> {
                String formatted = LogFormatUtils.formatValue(result, !traceOn);
                return "Resume with async result [" + formatted + "]";
            });
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        // 解析请求参数,调用controller方法
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        // 返回ModelAndView
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        // 标记请求完成
        webRequest.requestCompleted();
    }
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 调用处理方法
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    // 设置响应状态
    setResponseStatus(webRequest);

    // 返回值为空的处理
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            disableContentCachingIfNecessary(webRequest);
            // 表示请求处理完了,不需要视图解析
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    // 表示还没处理完
    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // 将返回值封装成ReturnValueMethodParameter,
        // 使用返回值解析器解析返回值
        this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 解析请求参数值
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    // 通过反射调用方法
    return doInvoke(args);
}
解析请求参数值
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 得到方法参数,MethodParameter封装了参数的所有信息
    MethodParameter[] parameters = getMethodParameters();
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }

    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = findProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        // 判断是否有参数解析器可以支持该参数
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
            // 解析参数
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);// 从请求中解析出当前参数的值
        }
        catch (Exception ex) {
            // Leave stack trace for later, exception may actually be resolved and handled...
            if (logger.isDebugEnabled()) {
                String exMsg = ex.getMessage();
                if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                    logger.debug(formatArgumentError(parameter, exMsg));
                }
            }
            throw ex;
        }
    }
    return args;
}

通过委托给已注册的HandlerMethodArgumentResolvers列表来解析方法参数。先判断是否有参数解析器可以支持该参数,resolvers是HandlerMethodArgumentResolverComposite类,也实现了HandlerMethodArgumentResolver接口,是一个组合解析器,它是一个代理,具体代理其余干活的那些参数解析器,持有所有的HandlerMethodArgumentResolver接口实现类。

@Override
public boolean supportsParameter(MethodParameter parameter) {
    // 看看是否有可以解析该参数的解析器
    return getArgumentResolver(parameter) != null;
}

@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    // 先从缓存中看看是否有该参数对应的解析器,因为第一次调完就将参数与参数解析器的对应关系
    // 缓存起来,后面再调就不需要重新判断了,直接将对应参数的解析器取出就行了
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        // 遍历所有解析器,判断是否支持,遇到一个支持就结束,并放到缓存中
        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
            if (resolver.supportsParameter(parameter)) {
                result = resolver;
                this.argumentResolverCache.put(parameter, result);
                break;
            }
        }
    }
    return result;
}

通过遍历所有的参数解析器来看看是不是支持解析该参数。
image.png
spring默认提供了26种参数解析器,用来解析各种参数类型,比如我们最常用的参数注解 @RequestParam 就是由 RequestParamMethodArgumentResolver 解析的,PathVariableMethodArgumentResolver 用来解析 @PathVariable 注解。

知道了有参数解析器可以解析当前的参数后,就使用该解析器来解析。跟上面一样,也是通过HandlerMethodArgumentResolverComposite来代理

@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    // 获取该参数对应的参数解析器,这个方法上面已经解析过了,这里就不细说了
    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unsupported parameter type [" +
                parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    }
    // 使用具体参数解析器解析
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

这里使用 RequestParamMethodArgumentResolver 来做一个解析。

RequestParamMethodArgumentResolver
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    // 获取给定方法参数的NamedValueInfo,就是解析@RequestParam注解,
    // 然后将参数名,是否必传以及默认值封装成NamedValueInfo
    NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
    MethodParameter nestedParameter = parameter.nestedIfOptional();

    // 解析出真正参数名,因为@RequestParam注解中的参数名有可能使用表达式来引用外部变量,
    // 比如@RequestParam("${param}") 这样,需要解析出${param}真正的参数名称
    Object resolvedName = resolveStringValue(namedValueInfo.name);
    if (resolvedName == null) {
        throw new IllegalArgumentException(
                "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
    }

    // 根据参数名从请求中解析出参数值
    Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
    if (arg == null) {
        // 如果没解析出来则用默认值,也是使用resolveStringValue方法,因为默认值也可以使用表达式
        if (namedValueInfo.defaultValue != null) {
            arg = resolveStringValue(namedValueInfo.defaultValue);
        }
        // 没有设置默认值,则再看该参数是否是必传的,如果是必传的则抛出异常
        else if (namedValueInfo.required && !nestedParameter.isOptional()) {
            handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
        }
        // 非必传则就使用null,如果是boolean类型,则是false
        arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
    }
    // 如果解析出来的值是空字符串"",但设置了默认值则使用默认值
    else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
        arg = resolveStringValue(namedValueInfo.defaultValue);
    }

    // 做类型转换,因为request中的参数统一都是String类型的,需要转换成需要的类型
    if (binderFactory != null) {
        WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
        try {
            arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
        }
        catch (ConversionNotSupportedException ex) {
            throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());
        }
        catch (TypeMismatchException ex) {
            throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());

        }
    }

    // 参数解析后的处理,这里面没有代码,由子类实现
    handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

    return arg;
}

看下如何从请求中解析出参数值

@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
    HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);

    // 处理文件上传
    if (servletRequest != null) {
        Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
        if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
            return mpArg;
        }
    }

    Object arg = null;
    MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
    if (multipartRequest != null) {
        List<MultipartFile> files = multipartRequest.getFiles(name);
        if (!files.isEmpty()) {
            arg = (files.size() == 1 ? files.get(0) : files);
        }
    }
    if (arg == null) {
        // 根据参数名从request获取到对应的值返回
        String[] paramValues = request.getParameterValues(name);
        if (paramValues != null) {
            arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
        }
    }
    return arg;
}

上面两个if都是处理文件上传的,最后一个if就是解析出参数值,直接根据参数名从request获取到对应的值返回。

参数解析出来后,就是通过反射调用Controller中的方法了。

调用Controller方法返回结果
@Nullable
protected Object doInvoke(Object... args) throws Exception {
    ReflectionUtils.makeAccessible(getBridgedMethod());
    try {
        // 直接通过反射调用
        return getBridgedMethod().invoke(getBean(), args);
    }
    catch (IllegalArgumentException ex) {
        assertTargetBean(getBridgedMethod(), getBean(), args);
        String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
        throw new IllegalStateException(formatInvokeError(text, args), ex);
    }
    catch (InvocationTargetException ex) {
        // Unwrap for HandlerExceptionResolvers ...
        Throwable targetException = ex.getTargetException();
        if (targetException instanceof RuntimeException) {
            throw (RuntimeException) targetException;
        }
        else if (targetException instanceof Error) {
            throw (Error) targetException;
        }
        else if (targetException instanceof Exception) {
            throw (Exception) targetException;
        }
        else {
            throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
        }
    }
}

接下来就是对返回值进行处理。

解析Controller方法返回值

与参数解析器一样,这里是使用返回值处理器处理,并且也是使用一个组合处理// 器来解析。HandlerMethodReturnValueHandlerComposite 持有所有的 HandlerMethodReturnValueHandler 接口实现类。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

    // 根据返回值和类型选择一个处理器
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    }
    // 使用具体处理器处理
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
    // 判断是不是异步返回值
    boolean isAsyncValue = isAsyncReturnValue(value, returnType);
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        // 如果是异步的但处理器不是用来处理异步的就跳过
        if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
            continue;
        }
        // 判断该处理器支不支持处理当前返回值类型
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

spring默认也提供了15种处理器,来处理各种返回值
image.png
比如 ModelAndViewMethodReturnValueHandler 用来处理返回 ModelAndView 的,ModelMethodProcessor 用来处理 Model类型的,RequestResponseBodyMethodProcessor 用来处理 @ResponseBody 注解的,而如果是返回一个视图名字符串,则使用 ViewNameMethodReturnValueHandler 处理。

这里以 ViewNameMethodReturnValueHandler 为例解析如何处理

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

    if (returnValue instanceof CharSequence) {
        String viewName = returnValue.toString();
        // 给ModelAndViewContainer这是视图名
        mavContainer.setViewName(viewName);
        // 判断是否是重定向,即以redirect:开头
        if (isRedirectViewName(viewName)) {
            mavContainer.setRedirectModelScenario(true);
        }
    }
    else if (returnValue != null) {
        // should not happen
        throw new UnsupportedOperationException("Unexpected return type: " +
                returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
    }
}

如果是 RequestResponseBodyMethodProcessor 则使用输出流将结果响应回客户端,不会有后面的视图解析。

返回ModelAndView

这里开始到后面的处理都是针对需要视图返回的

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
        ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

    modelFactory.updateModel(webRequest, mavContainer);
    // 已经处理完了就返回null
    if (mavContainer.isRequestHandled()) {
        return null;
    }
    ModelMap model = mavContainer.getModel();
    // 根据视图名,模型和状态新建一个ModelAndView返回
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
    if (!mavContainer.isViewReference()) {
        mav.setView((View) mavContainer.getView());
    }
    if (model instanceof RedirectAttributes) {
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (request != null) {
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
    }
    return mav;
}

解析以及渲染视图

这是最后一步了,通过视图解析器将视图名解析成具体的某一资源,比如jsp页面。
最终底层就是调用request.getRequestDispatcher(path).forward(request, response)来返回页面。
image.png
image.png
image.png
image.png
image.png
就不再具体解析了,知道就行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值