一、SpringMvc 初始化

1、SpringMvc 九大组件

  1. HandlerMapping: 处理器映射器

HandlerMapping是一个接口,内部只有一个方法,用来查找Handler的。在SpringMVC中会有很多请求,每个请求都需要一个Handler处理,它的作用是根据request找到对应的Handler。—用来查找Handler的

  1. DispatcherServlet.properties
  2. HandlerMapping=
  3. BeanNameUrlHandlerMapping; 根据配置文件中的beanID 进行识别
  4. RequestMappingHandlerMapping; 根据controller中的 @Controller 注解进行识别
  1. HandlerAdapter 适配器。

因为SpringMVC中的Handler可以是任意的形式,只要能处理请求就可以,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情。任意形式的Handler通过使用适配器,可以“转换”成固定形式,然后交给Servlet来处理。每种Handler都要有对应的HandlerAdapter才能处理请求。

  1. HandlerExceptionResolver

SpringMVC中专门负责处理异常的类,根据异常设置ModelAndView。之后交给render方法进行渲染,HandlerExceptionResolver只能处理页面渲染之前的异常,页面渲染过程中的异常,它是不能处理的。

  1. ViewResolver

这个接口只有一个方法:View resolveViewName(String viewName, Locale locale)throws Exception; 用来将String类型的视图名(viewName)和Locale解析为View类型的视图。

  1. RequestToViewNameTranslator

    从request中获取ViewName

  2. LocaleResolver

有两个参数:一是视图名(viewName),另一个是Locale。视图名是处理器返回的,但是Locale没有,而LocaleResolver就是用于从request解析出Locale。

  1. ThemeResolver

解析主题

  1. MultipartResolver

用于处理上传请求,将普通的request包装成MultipartHttpServletRequest,就可以调用getFile方法获取File

  1. FlashMapManager

管理FlashMap的,FlashMap是用在redirect中传递参数。 就像我们写接口传递的参数,可以封装到Map中

2、SpringMvc 启动流程

思考:当我们使用SpringMVC 的时候,相当于在spring框架的基础之上做了一个扩展工作而已,我们自己来实现扩展,可以从哪里下手?

  1. SpringMvc 是Spring的扩展,那么在启动springmvc之前必须要先把spring 容器启动起来。

2.1 SpringMVC 启动基本流程

  1. 首先启动tomact 容器,读取 web.xml 中对应对配置文件,核心配置文件有:
    1. spring-config.xml 启动Spring容器使用的
    2. applicationContextx.xml 启动SpringMvc 容器使用的
  2. 然后启动Spring 容器,生产Spring中内置的一些bean 对象
  3. 最后启动SpringMvc 容器,创建SpringMvc 特有对bean 对象

2.2 源码分析启动流程

2.2.1 启动tomact 容器

第一步:启动tomact 容器,详细内容等后期看tomcat 源码专题系列分析

2.2.1 启动Spring 容器

第二步:启动Spring 容器

  1. 读取web.xml配置文件

    在原始Spring 项目中,会有web.xml 配置文件, 在配置文件中会进行一个listener 配置

    1. <listener>
    2. <listener-class>org.springframework.web.context.ContextLoaderListener</linstener-class>
    3. </listener>
  2. 调用 initWebApplicationContext() 方法

    1. ContextLoaderListener 调用 initWebApplicationContext() 方法作为启动容器的入口
    2. �ContextLoaderListener 类继承了ContextLoader类,实现ServletContextListener接口
    3. 实现 contextInitialized() 方法,在该方法中调用了父类的 initWebApplicationContext() 方法 ```json /**
    • 引导 监听器启动 和关 闭Spring的根 WebApplicationContext。
    • 简单地委托给ContextLoader和ContextCleanupListener
    • Bootstrap listener to start up and shut down Spring’s root {@link WebApplicationContext}.
    • Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
    • */ public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

      /**

      • Create a new {@code ContextLoaderListener} that will create a web application
      • context based on the “contextClass” and “contextConfigLocation” servlet
      • context-params. See {@link ContextLoader} superclass documentation for details on
      • default values for each.

    *创建一个新的{@code ContextLoaderListener}来创建一个web应用程序上下文基,于“contextClass”和“contextConfigLocation”servlet上下文参数。

    • This constructor is typically used when declaring {@code ContextLoaderListener}

    • as a {@code } within {@code web.xml}, where a no-arg constructor is
    • required.
    • The created application context will be registered into the ServletContext under

    • the attribute name {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
    • and the Spring application context will be closed when the {@link #contextDestroyed}
    • lifecycle method is invoked on this listener.
    • @see ContextLoader
    • @see #ContextLoaderListener(WebApplicationContext)
    • @see #contextInitialized(ServletContextEvent)
    • @see #contextDestroyed(ServletContextEvent) */ public ContextLoaderListener() { }

      /**

    • Create a new {@code ContextLoaderListener} with the given application context. This
    • constructor is useful in Servlet 3.0+ environments where instance-based
    • registration of listeners is possible through the {@link javax.servlet.ServletContext#addListener}
    • API.
    • The context may or may not yet be {@linkplain

    • org.springframework.context.ConfigurableApplicationContext#refresh() refreshed}. If it
    • (a) is an implementation of {@link ConfigurableWebApplicationContext} and
    • (b) has not already been refreshed (the recommended approach),
    • then the following will occur:
      • If the given context has not already been assigned an {@linkplain
      • org.springframework.context.ConfigurableApplicationContext#setId id}, one will be assigned to it
      • {@code ServletContext} and {@code ServletConfig} objects will be delegated to
      • the application context
      • {@link #customizeContext} will be called
      • Any {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}s
      • specified through the “contextInitializerClasses” init-param will be applied.
      • {@link org.springframework.context.ConfigurableApplicationContext#refresh refresh()} will be called
    • If the context has already been refreshed or does not implement
    • {@code ConfigurableWebApplicationContext}, none of the above will occur under the
    • assumption that the user has performed these actions (or not) per his or her
    • specific needs.
    • See {@link org.springframework.web.WebApplicationInitializer} for usage examples.

    • In any case, the given application context will be registered into the

    • ServletContext under the attribute name {@link
    • WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and the Spring
    • application context will be closed when the {@link #contextDestroyed} lifecycle
    • method is invoked on this listener.
    • @param context the application context to manage
    • @see #contextInitialized(ServletContextEvent)
    • @see #contextDestroyed(ServletContextEvent) */ public ContextLoaderListener(WebApplicationContext context) { super(context); }
  1. /**
  2. * Initialize the root web application context.
  3. */
  4. @Override
  5. public void contextInitialized(ServletContextEvent event) {
  6. initWebApplicationContext(event.getServletContext());
  7. }
  8. /**
  9. * Close the root web application context.
  10. */
  11. @Override
  12. public void contextDestroyed(ServletContextEvent event) {
  13. closeWebApplicationContext(event.getServletContext());
  14. ContextCleanupListener.cleanupAttributes(event.getServletContext());
  15. }

}

  1. 3. 调用configureAndRefreshWebApplicationContext() 方法
  2. 1. 根据ServletContext 创建得到 ConfigurableWebApplicationConext对象
  3. 1. 根据 上面提供的两个对象,调用 configureAndRefreshWebApplicationContext() 方法
  4. ```json
  5. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
  6. try {
  7. // Store context in local instance variable, to guarantee that
  8. // it is available on ServletContext shutdown.
  9. if (this.context == null) {
  10. // 创建对象 context为 WebApplicationContext
  11. this.context = createWebApplicationContext(servletContext);
  12. }
  13. if (this.context instanceof ConfigurableWebApplicationContext) {
  14. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
  15. if (!cwac.isActive()) {
  16. // The context has not yet been refreshed -> provide services such as
  17. // setting the parent context, setting the application context id, etc
  18. if (cwac.getParent() == null) {
  19. // The context instance was injected without an explicit parent ->
  20. // determine parent for root web application context, if any.
  21. ApplicationContext parent = loadParentContext(servletContext);
  22. cwac.setParent(parent);
  23. }
  24. // 调用真正的方法
  25. configureAndRefreshWebApplicationContext(cwac, servletContext);
  26. }
  27. }
  28. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  29. 中间代码省去了,为了篇幅和效果,可以直接查看源码
  30. return this.context;
  31. }
  1. wac.refresh()方法,ConfigurableApplicationContext接口refresh()方法,完成Spring 容器的启动

    1. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    2. // 调用 Spring 的核心方法入口开始 bean的创建流程
    3. wac.refresh();
    4. }
  2. 完成Sprig容器的启动

    2.2.3 启动SpringMvc 容器

    第三步: 启动SpringMvc 容器

    SpringMvc 的本质就是一个Servlet,在web.xml 配置文件中定义了DispatcherServlet 这个类,所以SpringMvc 的启动就是找到init()方法,但是DispatcherServlet 类中没有init()方法,思路就是一步一步找到父类中的init()方法。tomcat 读取到web.xml 文件后,会读取到servlet 的配置,然后进行执行Servlet的构造方法,最后调用init()方法,具体源码在 tomact 源码中 启动SpringMvc 容器:在启动SpringMvc 容器的过程,就是执行DispatcherServlet 生命周期

  3. 调用Servlet 的init() 方法

    1. DisPatcherServlet 继承了FrameworkServlet,FrameworkServlet 继承了HttpServletBean,调用的Servlet的init()方法实际就是 HttpServletBean的 init 方法,此时就是通过创建生成的DispatcherServelt对象调用的

      1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/12747730/1652528196119-b94ba63b-92bc-4182-ab93-91414349d63d.png#clientId=u681eaa93-d1f2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=333&id=ufa884f9e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=998&originWidth=1668&originalType=binary&ratio=1&rotation=0&showTitle=false&size=85375&status=done&style=none&taskId=u16cf6b52-bb66-4181-83e5-feeb340c894&title=&width=557)
      1. @SuppressWarnings("serial")
      2. public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
      3. @Override
      4. public final void init() throws ServletException {
      5. if (logger.isDebugEnabled()) {
      6. logger.debug("Initializing servlet '" + getServletName() + "'");
      7. }
      8. // Set bean properties from init parameters.
      9. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
      10. if (!pvs.isEmpty()) {
      11. try {
      12. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
      13. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
      14. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
      15. initBeanWrapper(bw);
      16. bw.setPropertyValues(pvs, true);
      17. }
      18. catch (BeansException ex) {
      19. if (logger.isErrorEnabled()) {
      20. logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
      21. }
      22. throw ex;
      23. }
      24. }
      25. // Let subclasses do whatever initialization they like.
      26. initServletBean();
      27. if (logger.isDebugEnabled()) {
      28. logger.debug("Servlet '" + getServletName() + "' configured successfully");
      29. }
      30. }
      31. }
  4. 调用 FrameworkServlet 类的 initServletBean()方法

    1. public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    2. @Override
    3. protected final void initServletBean() throws ServletException {
    4. // 记录开始时间
    5. long startTime = System.currentTimeMillis();
    6. try {
    7. // 创建或刷新WebApplicationContext实例并对Servlet功能所有的变量进行初始化
    8. this.webApplicationContext = initWebApplicationContext();
    9. initFrameworkServlet();
    10. }
    11. catch (ServletException | RuntimeException ex) {
    12. logger.error("Context initialization failed", ex);
    13. throw ex;
    14. }
    15. }
    16. }
  5. 调用 initWebApplicationContext() 方法

    1. public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    2. protected WebApplicationContext initWebApplicationContext() {
    3. // 获取得到 WebApplicationContext 对象
    4. WebApplicationContext rootContext =
    5. WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    6. WebApplicationContext wac = null;
    7. //如果构造方法中已经传入webApplicationContext 属性,则直接使用
    8. if (this.webApplicationContext != null) {
    9. // A context instance was injected at construction time -> use it
    10. wac = this.webApplicationContext;
    11. if (wac instanceof ConfigurableWebApplicationContext) {
    12. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
    13. if (!cwac.isActive()) {
    14. // The context has not yet been refreshed -> provide services such as
    15. // setting the parent context, setting the application context id, etc
    16. if (cwac.getParent() == null) {
    17. // The context instance was injected without an explicit parent -> set
    18. // the root application context (if any; may be null) as the parent
    19. cwac.setParent(rootContext);
    20. }
    21. configureAndRefreshWebApplicationContext(cwac);
    22. }
    23. }
    24. }
    25. // 方式二
    26. if (wac == null) {
    27. wac = findWebApplicationContext();
    28. }
    29. // 方式三,一般使用方式三,因为在方式一、二中是找不对象
    30. if (wac == null) {
    31. wac = createWebApplicationContext(rootContext);
    32. }
    33. // 由具体的实现类进行实现,SpringMvc9大组件就是在这里调用该方法,在子类中实现的,后面有详细分析
    34. if (!this.refreshEventReceived) {
    35. // Either the context is not a ConfigurableApplicationContext with refresh
    36. // support or the context injected at construction time had already been
    37. // refreshed -> trigger initial onRefresh manually here.
    38. synchronized (this.onRefreshMonitor) {
    39. onRefresh(wac);
    40. }
    41. }
    42. if (this.publishContext) {
    43. // Publish the context as a servlet context attribute.
    44. String attrName = getServletContextAttributeName();
    45. getServletContext().setAttribute(attrName, wac);
    46. if (this.logger.isDebugEnabled()) {
    47. this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
    48. "' as ServletContext attribute with name [" + attrName + "]");
    49. }
    50. }
    51. return wac;
    52. }
    53. }
  6. 调用configureAndRefreshWebApplicationContext() 方法,进入到Spring的启动流程中

    1. 入口为上面initWebApplication 中的createWebApplicationContext()方法

      1. protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
      2. Class<?> contextClass = getContextClass();
      3. if (this.logger.isDebugEnabled()) {
      4. this.logger.debug("Servlet with name '" + getServletName() +
      5. "' will try to create custom WebApplicationContext context of class '" +
      6. contextClass.getName() + "'" + ", using parent context [" + parent + "]");
      7. }
      8. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
      9. throw new ApplicationContextException(
      10. "Fatal initialization error in servlet with name '" + getServletName() +
      11. "': custom WebApplicationContext class [" + contextClass.getName() +
      12. "] is not of type ConfigurableWebApplicationContext");
      13. }
      14. ConfigurableWebApplicationContext wac =
      15. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
      16. wac.setEnvironment(getEnvironment());
      17. wac.setParent(parent);
      18. String configLocation = getContextConfigLocation();
      19. if (configLocation != null) {
      20. wac.setConfigLocation(configLocation);
      21. }
      22. configureAndRefreshWebApplicationContext(wac);
      23. return wac;
      24. }
  7. wac.refresh();

    1. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    2. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
    3. // The application context id is still set to its original default value
    4. // -> assign a more useful id based on available information
    5. if (this.contextId != null) {
    6. wac.setId(this.contextId);
    7. }
    8. else {
    9. // Generate default id...
    10. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
    11. ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
    12. }
    13. }
    14. wac.setServletContext(getServletContext());
    15. wac.setServletConfig(getServletConfig());
    16. wac.setNamespace(getNamespace());
    17. wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
    18. // The wac environment's #initPropertySources will be called in any case when the context
    19. // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    20. // use in any post-processing or initialization that occurs below prior to #refresh
    21. ConfigurableEnvironment env = wac.getEnvironment();
    22. if (env instanceof ConfigurableWebEnvironment) {
    23. ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    24. }
    25. postProcessWebApplicationContext(wac);
    26. applyInitializers(wac);
    27. wac.refresh();
    28. }

    3. SpringMvc 的核心类-DispatherServlet

    SpringMvc 实质就是一个servlet,即DispatcherServlet。DispatcherServlet 继承和实现多个父类和父接口。最顶层的接口就是Servlet。从一开始接触Servlet知道,Servlet最核心的就是它的生命周期,初始化 -> 处理请求-> 销毁。
    来看一下Servlet接口和对应的方法: ```java

package javax.servlet;

import java.io.IOException;

/**

  • Defines methods that all servlets must implement. /

public interface Servlet {

  1. public void init(ServletConfig config) throws ServletException;
  2. public ServletConfig getServletConfig();
  3. public void service(ServletRequest req, ServletResponse res)
  4. throws ServletException, IOException;
  5. public String getServletInfo();
  6. public void destroy();

}

  1. 看到servlet 最核心的方法: init() 初始化, service() 处理请求,destroy() 销毁。 SpringMvc中,核心类DispatcherServlet 继承了 HttpServletBean, 实现了其父类的init() 方法,完成整个SpringMvc的启动和9大内置对象的加载。<br />继续跟进HttpServletBean类的init()方法,可以看到调用了本类中的initServletBean()方法,由具体子类实现
  2. ```java
  3. public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
  4. public final void init() throws ServletException {
  5. // Let subclasses do whatever initialization they like.
  6. initServletBean();
  7. }
  8. protected void initServletBean() throws ServletException {
  9. }

initServletBean() 方法的唯一实现者,即DisPatchServlet的夫类FrameworkServlet。详细看一下这个类的实现,也是SpringMvc 的核心入口方法调用:

  1. protected WebApplicationContext initWebApplicationContext() {
  2. // 获取得到 WebApplicationContext 对象
  3. WebApplicationContext rootContext =
  4. WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  5. WebApplicationContext wac = null;
  6. //如果构造方法中已经传入webApplicationContext 属性,则直接使用
  7. if (this.webApplicationContext != null) {
  8. // A context instance was injected at construction time -> use it
  9. wac = this.webApplicationContext;
  10. if (wac instanceof ConfigurableWebApplicationContext) {
  11. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
  12. if (!cwac.isActive()) {
  13. // The context has not yet been refreshed -> provide services such as
  14. // setting the parent context, setting the application context id, etc
  15. if (cwac.getParent() == null) {
  16. // The context instance was injected without an explicit parent -> set
  17. // the root application context (if any; may be null) as the parent
  18. cwac.setParent(rootContext);
  19. }
  20. configureAndRefreshWebApplicationContext(cwac);
  21. }
  22. }
  23. }
  24. if (wac == null) {
  25. wac = findWebApplicationContext();
  26. }
  27. // 第一步: 创建webApplicationContenxt 对象,一步一步调用最后执行到Spring 的refresh()方法
  28. if (wac == null) {
  29. wac = createWebApplicationContext(rootContext);
  30. }
  31. if (!this.refreshEventReceived) {
  32. // Either the context is not a ConfigurableApplicationContext with refresh
  33. // support or the context injected at construction time had already been
  34. // refreshed -> trigger initial onRefresh manually here.
  35. // 第二步: 调用SpingMvc的onRefresh()方法,进行9大内置对象的加载
  36. synchronized (this.onRefreshMonitor) {
  37. onRefresh(wac);
  38. }
  39. }
  40. //.........
  41. return wac;
  42. }

跟进两个核心方法一:createWebApplicationContext()方法,发现最后执行到configableAndRefreshWebApplicationContext()方法中,

  1. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
  2. //.........
  3. wac.setServletContext(getServletContext());
  4. wac.setServletConfig(getServletConfig());
  5. wac.setNamespace(getNamespace());
  6. wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
  7. // 第一步: 初始化环境资源
  8. ConfigurableEnvironment env = wac.getEnvironment();
  9. if (env instanceof ConfigurableWebEnvironment) {
  10. ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
  11. }
  12. //
  13. postProcessWebApplicationContext(wac);
  14. //
  15. applyInitializers(wac);
  16. //第四步: 调用Spring的底层方法
  17. wac.refresh();
  18. }

根据核心方法二:onRefresh()方法,在FrameworkServlet 类中是一个抽象方法没有具体的实现,再一步跟进发现他最后的实现类是DispatcherServlet,也就是SpringMvc的核心类之一,根据代码如下:

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

二、SpringMvc 请求处理流程

一、SpringMvc 基本流程

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/12747730/1652152113936-1352bd69-50f2-4a77-a41e-a2b80e3b47ff.png#clientId=u7e5a1957-ab4f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=322&id=ue8ba96c1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=644&originWidth=1184&originalType=binary&ratio=1&rotation=0&showTitle=false&size=196149&status=done&style=none&taskId=u623f2086-8427-49ca-847a-9db18d2152b&title=&width=592)<br />①:DispatcherServlet是SpringMVC中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件。<br />②:HanlerMapping是SpringMVC中完成url到Controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller.<br />③:Cntroller处理request,并返回ModelAndView对象,Controller是SpringMVC中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件。<br />④ ⑤ ⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端。

二、请求处理流程

2.1 请求处理流程

  1. 浏览器发起请求
  2. 进入到FrameworkServlet 类中(DispatcherServlet 的父类),执行service 方法
    1. 获得请求方式(get、post、或者其他)
    2. super.service
    3. 根据请求方式进行执行 doXXX()方法,每个doXXX()方法中调用同一个方法,processRequest()
    4. processRequest()方法执行处理
    5. doService(request,response)
  3. 执行DispathcherServlet.doService()
    1. 逻辑处理和一些属性设置
    2. 调用doDispatcher 方法
  4. 执行doDispatcher()方法,最核心和最关键的方法

    1. checkMultipart()检查请求是否为文件上传请求
    2. getHandler() 获取请求对应的HandlerExecutionChain 对象(根据请求获取到对应处理请求的Controller 类和具体处理请求的方法)
    3. getHandlerAdapter(mapperHandler.getHadler()) 获取处理器适配器
    4. getLastModified() 获取请求中服务器最后被修改时间
    5. mv = ha.handle(); 真正的调用handle 方法,也就是执行对应的方法,并返回视图。
    6. processDispatchResult(); // 处理返回结果,包括处理异常,渲染页面,触发Interceptor的afterCompletion

      2.2 请求处理流程源码分析

      1. 浏览器发起请求

      2.代码调用执行到FrameWorkServlet 中的 service() 方法中

    7. 获取方法,得到请求方式

    8. 调用 super.service()方法,因为详细的调用进入到了tomcat中 ```json @Override protected void service(HttpServletRequest request, HttpServletResponse response)

      1. throws ServletException, IOException {

      HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) {

      1. processRequest(request, response);

      } else {

      1. super.service(request, response);

      } }

  1. > **在FrameworkServlet 中有多个doXXX()方法,其实每个方法都是对应的一种请求处理方式,创建的请求方式有:getpostdelete等,也就相应的存在 doGet()方法、doPost()方法等。**
  2. > 1. **所有的doXXX() 方法中都调用了同一个方法 processRequest(request,response)**
  3. > 1. **processRequest 方法都统一调用了一个doService()方法**
  4. 3. 调用FrameWorkServletdoGet()方法
  5. ```json
  6. @Override
  7. protected final void doGet(HttpServletRequest request, HttpServletResponse response)
  8. throws ServletException, IOException {
  9. processRequest(request, response);
  10. }
  1. 调用 doServcie()方法 ```json protected final void processRequest(HttpServletRequest request, HttpServletResponse response)

    1. throws ServletException, IOException {

    long startTime = System.currentTimeMillis(); Throwable failureCause = null;

    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request);

    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    initContextHolders(request, localeContext, requestAttributes);

    try {

    1. doService(request, response);

    } catch (ServletException | IOException ex) {

    1. failureCause = ex;
    2. throw ex;

    } catch (Throwable ex) {

    1. failureCause = ex;
    2. throw new NestedServletException("Request processing failed", ex);

    }

    finally {

    1. resetContextHolders(request, previousLocaleContext, previousAttributes);
    2. if (requestAttributes != null) {
    3. requestAttributes.requestCompleted();
    4. }
    5. if (logger.isDebugEnabled()) {
    6. if (failureCause != null) {
    7. this.logger.debug("Could not complete request", failureCause);
    8. }
    9. else {
    10. if (asyncManager.isConcurrentHandlingStarted()) {
    11. logger.debug("Leaving response open for concurrent processing");
    12. }
    13. else {
    14. this.logger.debug("Successfully completed request");
    15. }
    16. }
    17. }
    18. publishRequestHandledEvent(request, response, startTime, failureCause);

    } }

  1. <a name="tpDwQ"></a>
  2. #### 3. 调用DispatcherServlet中的 doService()方法主要通过子类实现完成
  3. ```json
  4. @Override
  5. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  6. //........
  7. try {
  8. doDispatch(request, response);
  9. }
  10. // ........
  11. }

4.执行完成SpringMvc 核心方法doDispatcher()

  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. HttpServletRequest processedRequest = request;
  3. HandlerExecutionChain mappedHandler = null;
  4. boolean multipartRequestParsed = false;
  5. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  6. try {
  7. ModelAndView mv = null;
  8. Exception dispatchException = null;
  9. try {
  10. // 检查是否是文件上传的请求
  11. processedRequest = checkMultipart(request);
  12. multipartRequestParsed = (processedRequest != request);
  13. // Determine handler for the current request.
  14. // 2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handlerinterceptors.
  15. mappedHandler = getHandler(processedRequest);
  16. if (mappedHandler == null) {
  17. noHandlerFound(processedRequest, response);
  18. return;
  19. }
  20. // Determine handler adapter for the current request.
  21. // 3.获取得到 适配器
  22. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  23. // Process last-modified header, if supported by the handler.
  24. String method = request.getMethod();
  25. boolean isGet = "GET".equals(method);
  26. if (isGet || "HEAD".equals(method)) {
  27. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  28. if (logger.isDebugEnabled()) {
  29. logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
  30. }
  31. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  32. return;
  33. }
  34. }
  35. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  36. return;
  37. }
  38. // 4. 执行处理并返回ModleAndView
  39. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  40. if (asyncManager.isConcurrentHandlingStarted()) {
  41. return;
  42. }
  43. applyDefaultViewName(processedRequest, mv);
  44. mappedHandler.applyPostHandle(processedRequest, response, mv);
  45. }
  46. catch (Exception ex) {
  47. dispatchException = ex;
  48. }
  49. catch (Throwable err) {
  50. // As of 4.3, we're processing Errors thrown from handler methods as well,
  51. // making them available for @ExceptionHandler methods and other scenarios.
  52. dispatchException = new NestedServletException("Handler dispatch failed", err);
  53. }
  54. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  55. }
  56. catch (Exception ex) {
  57. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  58. }
  59. catch (Throwable err) {
  60. triggerAfterCompletion(processedRequest, response, mappedHandler,
  61. new NestedServletException("Handler processing failed", err));
  62. }
  63. finally {
  64. if (asyncManager.isConcurrentHandlingStarted()) {
  65. // Instead of postHandle and afterCompletion
  66. if (mappedHandler != null) {
  67. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  68. }
  69. }
  70. else {
  71. // Clean up any resources used by a multipart request.
  72. if (multipartRequestParsed) {
  73. cleanupMultipart(processedRequest);
  74. }
  75. }
  76. }
  77. }

2.3 doDispatcher 处理流程

《doDispatch流程图》:
doDispatch() 执行流程.png

三、SpringMvc 工作机制

  1. 在容器初始化时会建立所有urlcontroller的对应关系,保存到Map<url,controller>中。tomcat启动时会通知spring初始化容器(加载bean的定义信息和初始化所有单例bean),然后springmvc会遍历容器中的bean,获取每一个controller中的所有方法访问的url,然后将urlController保存到一个Map中;

这样就可以根据request快速定位到Controller,因为最终处理request的是Controller中的方法,Map中只保留了url和Controller中的对应关系,所以要根据request的url进一步确认Controller中的method,这一步工作的原理就是拼接Controller的url(Controller上@RequestMapping的值)和方法的url(method上@RequestMapping的值),与request的url进行匹配,找到匹配的那个方法;

确定处理请求的method后,接下来的任务就是参数绑定,把request中参数绑定到方法的形式参数上,这一步是整个请求处理过程中最复杂的一个步骤。SpringMVC提供了两种request参数与方法形参的绑定方法:
① 通过注解进行绑定 @RequestParam
② 通过参数名称进行绑定.
使用注解进行绑定,我们只要在方法参数前面声明@RequestParam(“a”),就可以将request中参数a的值绑定到方法的该参数上。使用参数名称进行绑定的前提是必须要获取方法中参数的名称,Java反射只提供了获取方法的参数的类型,并没有提供获取参数名称的方法。SpringMVC解决这个问题的方法是用asm框架读取字节码文件,来获取方法的参数名称。asm框架是一个字节码操作框架,关于asm更多介绍可以参考它的官网。个人建议,使用注解来完成参数绑定,这样就可以省去asm框架的读取字节码的操作。

四、源码分析

根据工作机制三部分来分析SpringMVC的源码:
其一 ApplicationContext初始化时建立所有url和controller类的对应关系(用Map保存);
其二 根据请求url找到对应的controller,并从controller中找到处理请求的方法;
其三 request参数绑定到方法的形参,执行方法处理请求,并返回结果视图;

第一步:SpringMVC 的初始化

1. 初始化Springmvc 9大组件

  1. 初始化入口:initWebApplicationContext ```json
  2. FrameworkServlet.initWebApplicationContext(){ onRefresh(wac) }
    ```

  3. 初始化9大组件流程 ```json

  4. DispatcherSerclet extends FrameworkServlet
    实现 FrameworkServlet 类的 onRefresh() 方法

    @Override protected void onRefresh(ApplicationContext context) {

    1. initStrategies(context);

    } /**

    • Initialize the strategy objects that this servlet uses.
    • May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { // 初始化 initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); // 初始化 HandlerMappings:映射器,用来将对应的request 请求跟controller进行对应 initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); } ```

      2. 初始化处理器映射器,建立Map的关系

  5. initHandlerMappings =》 BeanNameUrlHandlerMapping ```json

  6. 第一步:getDefaultStrategies private void initHandlerMappings(ApplicationContext context) {
    1. this.handlerMappings = null;
    2. // Ensure we have at least one HandlerMapping, by registering
    3. // a default HandlerMapping if no other mappings are found.
    4. if (this.handlerMappings == null) {
    5. this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    6. if (logger.isDebugEnabled()) {
    7. logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
    8. }
    9. }
    }
  7. 第二步:createDefaultStrategy /**
    • Create a List of default strategy objects for the given strategy interface.
    • The default implementation uses the “DispatcherServlet.properties” file (in the same

    • package as the DispatcherServlet class) to determine the class names. It instantiates
    • the strategy objects through the context’s BeanFactory.
    • @param context the current WebApplicationContext
    • @param strategyInterface the strategy interface
    • @return the List of corresponding strategy objects */ @SuppressWarnings(“unchecked”) protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) { String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); if (value != null) {
      1. String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
      2. List<T> strategies = new ArrayList<>(classNames.length);
      3. for (String className : classNames) {
      4. try {
      5. Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
      // 核心方法
      1. Object strategy = createDefaultStrategy(context, clazz);
      2. strategies.add((T) strategy);
      3. }
      4. catch (ClassNotFoundException ex) {
      5. throw new BeanInitializationException(
      6. "Could not find DispatcherServlet's default strategy class [" + className +
      7. "] for interface [" + key + "]", ex);
      8. }
      9. catch (LinkageError err) {
      10. throw new BeanInitializationException(
      11. "Unresolvable class definition for DispatcherServlet's default strategy class [" +
      12. className + "] for interface [" + key + "]", err);
      13. }
      14. }
      15. return strategies;
      } else {
      1. return new LinkedList<>();
      } }
  8. 第三步:createBean()

protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) { return context.getAutowireCapableBeanFactory().createBean(clazz); }

4.第四步:AbstractAutowireCapableBeanFactory.createBean() 进入到Spring源码中,创建bean的详细流程

5.第五步:doCreateBean() protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

  1. //1.创建bean对象 Instantiate the bean.
  2. instanceWrapper = createBeanInstance(beanName, mbd, args);
  3. //2. Initialize the bean instance.
  4. Object exposedObject = bean;
  5. try {
  6. //2. 填充属性
  7. populateBean(beanName, mbd, instanceWrapper);
  8. //3. 初始化bean
  9. exposedObject = initializeBean(beanName, exposedObject, mbd);
  10. }
  11. }

6.第六步: initializeBean protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

  1. // Aware接口处理器,调用BeanNameAware,BeanClassLoaderAware、beanFactoryAware
  2. invokeAwareMethods(beanName, bean);
  3. Object wrappedBean = bean;
  4. if (mbd == null || !mbd.isSynthetic()) {
  5. // 将BeanPostProcessors 应用到给定定现有Bean实列,调用他们的postProcessorBeforeInitialization初始化方法
  6. wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  7. }
  8. try {
  9. invokeInitMethods(beanName, wrappedBean, mbd);
  10. }
  11. return wrappedBean;

}

  1. applyBeanPostProcessorsBeforeInitialization @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)

    1. throws BeansException {
    2. Object result = existingBean;
    3. for (BeanPostProcessor processor : getBeanPostProcessors()) {
    4. Object current = processor.postProcessBeforeInitialization(result, beanName);
    5. if (current == null) {
    6. return result;
    7. }
    8. result = current;
    9. }
    10. return result;

    }

  2. ApplicationContextAwareProcessorpost.ProcessBeforeInitialization()

    @Override @Nullable public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {

    1. AccessControlContext acc = null;
    2. if (System.getSecurityManager() != null &&
    3. (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
    4. bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
    5. bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
    6. acc = this.applicationContext.getBeanFactory().getAccessControlContext();
    7. }
    8. if (acc != null) {
    9. AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
    10. invokeAwareInterfaces(bean);
    11. return null;
    12. }, acc);
    13. }
    14. else {
    15. invokeAwareInterfaces(bean);
    16. }
    17. return bean;

    }

  3. invokeAwareInterfaces private void invokeAwareInterfaces(Object bean) {
    1. if (bean instanceof Aware) {
    2. if (bean instanceof EnvironmentAware) {
    3. ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
    4. }
    5. if (bean instanceof EmbeddedValueResolverAware) {
    6. ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
    7. }
    8. if (bean instanceof ResourceLoaderAware) {
    9. ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
    10. }
    11. if (bean instanceof ApplicationEventPublisherAware) {
    12. ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
    13. }
    14. if (bean instanceof MessageSourceAware) {
    15. ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
    16. }
    17. if (bean instanceof ApplicationContextAware) {
    18. ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
    19. }
    20. }
    }
  4. ApplicationObjectSupport.setApplicationContext()

@Override public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { initApplicationContext(context); }

  1. 回到SpringMVC 源码中:#AbstractDetectingUrlHandlerMapping.initApplicationContext @Override public void initApplicationContext() throws ApplicationContextException {

    1. super.initApplicationContext();
    2. detectHandlers();

    }

    11.1 #AbstractHandlerMapping.initApplicationContext @Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }

    11.2#AbstractDetectingUrlHandlerMapping.detectHandlers()

    // 获取 applicationContext应用上下文

    1. ApplicationContext applicationContext = obtainApplicationContext();
    2. if (logger.isDebugEnabled()) {
    3. logger.debug("Looking for URL mappings in application context: " + applicationContext);
    4. }
    5. // 获取ApplicationContext容器中所有bean的Name
    6. String[] beanNames = (this.detectHandlersInAncestorContexts ?
    7. BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
    8. applicationContext.getBeanNamesForType(Object.class));
    9. // Take any bean name that we can determine URLs for.
    10. // 遍历beanNames,并找到这些bean对应的url
    11. for (String beanName : beanNames) {
    12. String[] urls = determineUrlsForHandler(beanName);
    13. if (!ObjectUtils.isEmpty(urls)) {
    14. // URL paths found: Let's consider it a handler.
    15. registerHandler(urls, beanName);
    16. }
    17. else {
    18. if (logger.isDebugEnabled()) {
    19. logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
    20. }
    21. }
    22. }

    }

  2. registerHandler()

//到此完成 BeanNameUrlHandlerMapping 的初始化 private final Map handlerMap = new LinkedHashMap<>(); handlerMap 中设置完成值

  1. 2. initHandlerMappings =》 RequestMappingHandlerMapping
  2. ```json
  3. public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
  4. }
  5. 1. #afterPropertiesSet
  6. @Override
  7. public void afterPropertiesSet() {
  8. initHandlerMethods();
  9. }
  10. 2. #initHandlerMethods
  11. /**
  12. * Scan beans in the ApplicationContext, detect and register handler methods.
  13. * @see #isHandler
  14. * @see #detectHandlerMethods
  15. * @see #handlerMethodsInitialized
  16. */
  17. protected void initHandlerMethods() {
  18. for (String beanName : beanNames) {
  19. if (beanType != null && isHandler(beanType)) {
  20. detectHandlerMethods(beanName);
  21. }
  22. }
  23. handlerMethodsInitialized(getHandlerMethods());
  24. }
  25. 3. #detectHandlerMethods
  26. protected void detectHandlerMethods(Object handler) {
  27. Class<?> handlerType = (handler instanceof String ?
  28. obtainApplicationContext().getType((String) handler) : handler.getClass());
  29. if (handlerType != null) {
  30. Class<?> userType = ClassUtils.getUserClass(handlerType);
  31. Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
  32. (MethodIntrospector.MetadataLookup<T>) method -> {
  33. try {
  34. return getMappingForMethod(method, userType);
  35. }
  36. catch (Throwable ex) {
  37. throw new IllegalStateException("Invalid mapping on handler class [" +
  38. userType.getName() + "]: " + method, ex);
  39. }
  40. });
  41. if (logger.isDebugEnabled()) {
  42. logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
  43. }
  44. methods.forEach((method, mapping) -> {
  45. Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
  46. registerHandlerMethod(handler, invocableMethod, mapping);
  47. });
  48. }
  49. }
  50. 4. #registerHandlerMethod
  51. protected void registerHandlerMethod(Object handler, Method method, T mapping) {
  52. this.mappingRegistry.register(mapping, handler, method);
  53. }
  54. 5. 完成基本属性值的设置
  1. class MappingRegistry {
  2. private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
  3. private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
  4. private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
  5. private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
  6. private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
  7. private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  8. }
  1. 上面的代码就是两种初始化HandlerMapping 的方式,但是在SpringMvc的实际应用中使用的都是第二种方式。我们首先看第一个步骤,也就是建立Map<url,controller>关系的部分。第一部分的入口类为ApplicationObjectSupportsetApplicationContext方法。setApplicationContext方法中核心部分就是初始化容器initApplicationContext(context),子类AbstractDetectingUrlHandlerMapping实现了该方法,所以我们直接看子类中的初始化容器方法。
  1. public void initApplicationContext() throws ApplicationContextException {
  2. super.initApplicationContext();
  3. detectHandlers();
  4. }
  5. /**
  6.    * 建立当前ApplicationContext中的所有controller和url的对应关系
  7.     */
  8. protected void detectHandlers() throws BeansException {
  9. ApplicationContext applicationContext = obtainApplicationContext();
  10. String[] beanNames = (this.detectHandlersInAncestorContexts ?
  11. BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
  12. applicationContext.getBeanNamesForType(Object.class));
  13. // Take any bean name that we can determine URLs for.
  14. for (String beanName : beanNames) {
  15. String[] urls = determineUrlsForHandler(beanName);
  16. if (!ObjectUtils.isEmpty(urls)) {
  17. // URL paths found: Let's consider it a handler.
  18. registerHandler(urls, beanName);
  19. }
  20. }
  21. if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
  22. logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
  23. }
  24. }
  25. /** 获取controller中所有方法的url,由子类实现,典型的模板模式 **/
  26.   protected abstract String[] determineUrlsForHandler(String beanName);

determineUrlsForHandler(String beanName)方法的作用是获取每个controller中的url

  1. /**
  2.    * 获取controller中所有的url
  3. */
  4.   protected String[] determineUrlsForHandler(String beanName) {
  5. // 获取ApplicationContext容器
  6.     ApplicationContext context = getApplicationContext();
  7. //从容器中获取controller
  8.      Class<?> handlerType = context.getType(beanName);
  9.      // 获取controller上的@RequestMapping注解
  10. RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
  11. if (mapping != null) { // controller上有注解
  12. this.cachedMappings.put(handlerType, mapping);
  13.        // 返回结果集
  14. Set<String> urls = new LinkedHashSet<String>();
  15.        // controller的映射url
  16. String[] typeLevelPatterns = mapping.value();
  17. if (typeLevelPatterns.length > 0) { // url>0
  18. // 获取controller中所有方法及方法的映射url
  19. String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
  20. for (String typeLevelPattern : typeLevelPatterns) {
  21. if (!typeLevelPattern.startsWith("/")) {
  22. typeLevelPattern = "/" + typeLevelPattern;
  23. }
  24. boolean hasEmptyMethodLevelMappings = false;
  25. for (String methodLevelPattern : methodLevelPatterns) {
  26. if (methodLevelPattern == null) {
  27. hasEmptyMethodLevelMappings = true;
  28. }
  29. else {
  30.                 // controller的映射url+方法映射的url
  31. String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
  32.                 // 保存到set集合中
  33.                  addUrlsForPath(urls, combinedPattern);
  34. }
  35. }
  36. if (hasEmptyMethodLevelMappings ||
  37. org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
  38. addUrlsForPath(urls, typeLevelPattern);
  39. }
  40. }
  41.          // 以数组形式返回controller上的所有url
  42. return StringUtils.toStringArray(urls);
  43. }
  44. else {
  45. // controller上的@RequestMapping映射url为空串,直接找方法的映射url
  46. return determineUrlsForHandlerMethods(handlerType, false);
  47. }
  48. } // controller上没@RequestMapping注解
  49. else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
  50. // 获取controller中方法上的映射url
  51. return determineUrlsForHandlerMethods(handlerType, false);
  52. }
  53. else {
  54. return null;
  55. }
  56. }

到这里HandlerMapping组件就已经建立所有url和controller的对应关系。

第二步:根据访问url找到对应controller中处理请求的方法

下面我们开始分析第二个步骤,第二个步骤是由请求触发的,所以入口为DispatcherServlet.DispatcherServlet的核心方法为doService(),doService()中的核心逻辑由doDispatch()实现,我们查看doDispatch()的源代码。

  1. /** 中央控制器,控制请求的转发 **/
  2. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  3. HttpServletRequest processedRequest = request;
  4. HandlerExecutionChain mappedHandler = null;
  5. int interceptorIndex = -1;
  6. try {
  7. ModelAndView mv;
  8. boolean errorView = false;
  9. try {
  10.          // 1.检查是否是文件上传的请求
  11. processedRequest = checkMultipart(request);
  12. // 2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handlerinterceptors.
  13. mappedHandler = getHandler(processedRequest, false);
  14.          // 如果handler为空,则返回404
  15. if (mappedHandler == null || mappedHandler.getHandler() == null) {
  16. noHandlerFound(processedRequest, response);
  17. return;
  18. }
  19. //3. 获取处理request的处理器适配器handler adapter
  20. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  21. // 处理 last-modified 请求头
  22. String method = request.getMethod();
  23. boolean isGet = "GET".equals(method);
  24. if (isGet || "HEAD".equals(method)) {
  25. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  26. if (logger.isDebugEnabled()) {
  27. String requestUri = urlPathHelper.getRequestUri(request);
  28. logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
  29. }
  30. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  31. return;
  32. }
  33. }
  34. // 4.拦截器的预处理方法
  35. HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
  36. if (interceptors != null) {
  37. for (int i = 0; i < interceptors.length; i++) {
  38. HandlerInterceptor interceptor = interceptors[i];
  39. if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
  40. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
  41. return;
  42. }
  43. interceptorIndex = i;
  44. }
  45. }
  46. // 5.实际的处理器处理请求,返回结果视图对象
  47. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  48. // 结果视图对象的处理
  49. if (mv != null && !mv.hasView()) {
  50. mv.setViewName(getDefaultViewName(request));
  51. }
  52. // 6.拦截器的后处理方法
  53. if (interceptors != null) {
  54. for (int i = interceptors.length - 1; i >= 0; i--) {
  55. HandlerInterceptor interceptor = interceptors[i];
  56. interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
  57. }
  58. }
  59. }
  60. catch (ModelAndViewDefiningException ex) {
  61. logger.debug("ModelAndViewDefiningException encountered", ex);
  62. mv = ex.getModelAndView();
  63. }
  64. catch (Exception ex) {
  65. Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
  66. mv = processHandlerException(processedRequest, response, handler, ex);
  67. errorView = (mv != null);
  68. }
  69. if (mv != null && !mv.wasCleared()) {
  70. render(mv, processedRequest, response);
  71. if (errorView) {
  72. WebUtils.clearErrorRequestAttributes(request);
  73. }
  74. }
  75. else {
  76. if (logger.isDebugEnabled()) {
  77. logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
  78. "': assuming HandlerAdapter completed request handling");
  79. }
  80. }
  81. // 请求成功响应之后的方法
  82. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
  83. }

第2步:getHandler(processedRequest)方法实际上就是从HandlerMapping中找到url和controller的对应关系。这也就是第一个步骤:建立Map的意义。我们知道,最终处理request的是controller中的方法,我们现在只是知道了controller,还要进一步确认controller中处理request的方法。由于下面的步骤和第三个步骤关系更加紧密,直接转到第三个步骤。

第三步、动态代理调用处理请求的方法,返回结果视图

上面的方法中,第2步其实就是从第一个步骤中的Map中取得Controller,然后经过拦截器的预处理方法,到最核心的部分—第5步调用controller的方法处理请求。在第2步中我们可以知道处理request的Controller,第5步就是要根据url确定Controller中处理请求的方法,然后通过反射获取该方法上的注解和参数,解析方法和参数上的注解,最后反射调用方法获取ModelAndView结果视图。因为上面采用注解url形式说明的,所以我们这里继续以注解处理器适配器来说明。第5步调用的就是AnnotationMethodHandlerAdapter的handle().handle()中的核心逻辑由invokeHandlerMethod(request, response, handler)实现。

  1. protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
  2. HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  3. ServletWebRequest webRequest = new ServletWebRequest(request, response);
  4. try {
  5. WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
  6. ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
  7. ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
  8. if (this.argumentResolvers != null) {
  9. invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  10. }
  11. if (this.returnValueHandlers != null) {
  12. invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
  13. }
  14. invocableMethod.setDataBinderFactory(binderFactory);
  15. invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
  16. ModelAndViewContainer mavContainer = new ModelAndViewContainer();
  17. mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
  18. modelFactory.initModel(webRequest, mavContainer, invocableMethod);
  19. mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
  20. AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
  21. asyncWebRequest.setTimeout(this.asyncRequestTimeout);
  22. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  23. asyncManager.setTaskExecutor(this.taskExecutor);
  24. asyncManager.setAsyncWebRequest(asyncWebRequest);
  25. asyncManager.registerCallableInterceptors(this.callableInterceptors);
  26. asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
  27. if (asyncManager.hasConcurrentResult()) {
  28. Object result = asyncManager.getConcurrentResult();
  29. mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
  30. asyncManager.clearConcurrentResult();
  31. LogFormatUtils.traceDebug(logger, traceOn -> {
  32. String formatted = LogFormatUtils.formatValue(result, !traceOn);
  33. return "Resume with async result [" + formatted + "]";
  34. });
  35. invocableMethod = invocableMethod.wrapConcurrentResult(result);
  36. }
  37. invocableMethod.invokeAndHandle(webRequest, mavContainer);
  38. if (asyncManager.isConcurrentHandlingStarted()) {
  39. return null;
  40. }
  41. return getModelAndView(mavContainer, modelFactory, webRequest);
  42. }
  43. finally {
  44. webRequest.requestCompleted();
  45. }
  46. }

五、总结

image.png