一.SpringMVC源码剖析-SpringMVC初始化

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: SpringMVC是基于Spring功能之上添加的Web框架,是市场上最流行的MVC框架,Spring MVC 是基于 Servlet 功能实现的,通过 Servlet 接口的 DispatcherServlet 来封装其核心功能实现,通过将请求分派给处理程序,同时带有可配置的处理程序映射、视图解析、本地语言、主题解析以及上载文件支持。在之前的《Spring源码剖析》系列文章中我们对Spring核心进行了分析,从这篇文章开始着手于SpringMVC源码分析。如果对你有所帮助,请使劲三连。

前言

SpringMVC是基于Spring功能之上添加的Web框架,是市场上最流行的MVC框架,Spring MVC 是基于 Servlet 功能实现的,通过 Servlet 接口的 DispatcherServlet 来封装其核心功能实现,通过将请求分派给处理程序,同时带有可配置的处理程序映射、视图解析、本地语言、主题解析以及上载文件支持。
在之前的《Spring源码剖析》系列文章中我们对Spring核心进行了分析,从这篇文章开始着手于SpringMVC源码分析。如果对你有所帮助,请使劲三连。

SpringMVC执行流程

SpringMVC执行流程几乎是在面试时面试官对SpringMVC部分的必问之题,下面是SpirngMVC的执行原理图
在这里插入图片描述
这个是请求在SpringMVC的执行流程

  1. DispatcherServlet:请求打过来由DispatcherServlet处理,它是 SpringMVC 中的前端控制器(中央控制器), 负责接收 Request 并将 Request 转发给对应的处理组件
  2. HandlerMapping:HandlerMapping 维护了 url 和 Controller(Handler)的 映 射关系 。 DispatcherServlet 接 收 请求, 然 后 从 HandlerMapping 查找处理请求的Controller(Handler),标注了@RequestMapping 的每个 method 都可以看成是一个 Handler,HandlerMapping 在请求到达之后, 它的作用便是找到请求相应的处理器 Handler 和 Interceptors。
  3. HandlerAdapter:SpringMVC通过HandlerAdapter对Handler进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。它的作用就是按照特定的规则去执行 Controller (Handler)
  4. Handler : Controller (Handler)负责处理请求,Controller 执行后并返回 ModelAndView 对象,其中包括了数据模型和逻辑视图,,ModelAndView 是封装结果 视图的组件。Handler把结果返回给HandlerAdapter,HandlerAdapter把结果返回给DispatcherServlet前端控制器。
  5. ViewResolver:DispatcherServlet收到ModelAndView,调用视图解析器(ViewResolver)来解析HandlerAdapter传递的ModelAndView。Handler执行完成后返回的是逻辑视图,也就是视图名字,一个String ,还有一个Model就是数据模型,封装成ModelAndView。ViewResolver视图解析的作用就是根据视图名,把本地模板文件(比如:xx.jsp;xx.ftl)解析为View视图对象。View用来渲染视图,也就是负责把Handler返回的数据模型model,填充到模板(jsp;ftl)形成html格式的静态内容。
  6. 最后就是把生成的html通过response写给浏览器,浏览器进行html渲染展示。

ContextLoaderListener 初始化XmlWebApplicationContext 容器

脱离WEB环境使用Spring我们可以直接用 new ClasspathXmlApplicationContext("配置文件.xml")来启动,但是在Web环境中我们需要把Spring和web结合,在SpringMVC中使用的容器工厂是XmlWebApplicationContext,ContextLoaderListener 的作用就是通过实现ServletContextListener 监听ServletContext初始化,然后创建XmlWebApplicationContext 容器工厂并设置到ServletContext上下文对象中。

在SpringMVC中我们需要在web.xml做如下配置

<!--告诉ContextLoadContextLoaderListener ,Spring文件的位置 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<servlet>
  <!--名称 -->
    <servlet-name>springmvc</servlet-name>
    <!-- 前端控制器 -->
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 启动顺序,数字越小,启动越早 -->
    <load-on-startup>1</load-on-startup>
    <init-param>
        <param-name></param-name>
        <param-value></param-value>
    </init-param>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<!--上下文监听器 -->
<listener>
   <listenerclass>
     org.springframework.web.context.ContextLoaderListener
   </listener-class>
</listener>
  • contextConfigLocation:通过context-param 配置contextConfigLocation ,值就是Spring的配置文件名,ContextLoaderListener会去加载这个配置文件。
  • DispatcherServlet:前端控制器,控制SpringMVC的请求处理过程
  • ContextLoaderListener :上下文监听器,负责在WEB容器启动时,自动装配ApplicationContext信息。

ContextLoaderListener实现了ServletContextListener ,它可以监听到ServletContext的contextInitialized初始化和contextDestroyed销毁事件。ServletContext是Servlet上下文对象,伴随着程序启动而创建,程序销毁而销毁,全局有效。我们也可以自定义ServletContextListener 的实现类来做我们自己的一些全局初始化工作。

ContextLoaderListener主要就是通过监听ServletContext的Initialized初始化,然后创建WebApplicationContext容器工厂,并添加到到ServletContext对象中。下面是ServletContextListener的源码

public interface ServletContextListener extends EventListener {
   
   

    public default void contextInitialized(ServletContextEvent sce) {
   
   
    }

    public default void contextDestroyed(ServletContextEvent sce) {
   
   
    }
}

下面是ContextLoaderListerner源码

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
   
   

    /**
     * Initialize the root web application context.
     */
    @Override
    public void contextInitialized(ServletContextEvent event) {
   
   
       //初始化容器
        initWebApplicationContext(event.getServletContext());
    }
}

初始化容器,并设置到ServletContext ,见:ContextLoader#initWebApplicationContext

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
   
   
    ...省略...
        try {
   
   


            if (this.context == null) {
   
   
                //【重要】这里在创建WebApplicationContext
                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.
                        ApplicationContext parent = loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }
            ...省略...
            //【重要】把WebApplicationContext放到servletContext
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

            ...省略...

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

ContextLoaderListener 调用 ContextLoader#initWebApplicationContext 器初始化容器对象,这里主要做了两个事情

  • 调用createWebApplicationContext创建上下文对象
  • 把WebApplicationContext设置到ServletContext中

跟进一下createWebApplicationContext方法,看一下是如何创建上下文对象的

   //【重要】加载ContextLoader.properties配置文件
   private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

   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.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());
        }
    }

    //【重要】 创建上下文对象
    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
   
   
        //【重要】这里在拿class
        Class<?> contextClass = determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
   
   
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }
        //使用BeanUtils.instantiateClass 根据class反射创建容器对象
        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

//找到容器对象的class
protected Class<?> determineContextClass(ServletContext servletContext) {
   
   
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
        if (contextClassName != null) {
   
   
            try {
   
   
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            }
            catch (ClassNotFoundException ex) {
   
   
                throw new ApplicationContextException(
                        "Failed to load custom context class [" + contextClassName + "]", ex);
            }
        }
        else {
   
   
        //【重要】从ContextLoader.properties加载class名
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
   
   
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            }
            catch (ClassNotFoundException ex) {
   
   
                throw new ApplicationContextException(
                        "Failed to load default context class [" + contextClassName + "]", ex);
            }
        }
    }

该方法主要就是从ContextLoader.properties加载容器对象的class和创建对象的过程

  • 先是通过ContextLoader的static静态代码块加载ContextLoader.properties配置得到一个Properties
  • 然后从Properties 中得到ContextClassName(使用的是XmlWebApplicationContext)返回
  • 然后通过BeanUtils.instantiateClass(contextClass)实例化对象
  • 最后把容器对象在设置到ServletContext

ContextLoadeer.properties中使用的是XmlWebApplicationContext ,配置文件内容如下
在这里插入图片描述

DispatcherServlet 初始化

首先来看一下DispatcherServlet的继承体系
在这里插入图片描述
DispatcherServlet是一个Servlet,拥有Servlet的生命周期,在Servlet初始化阶段会调用init方法,见其父类org.springframework.web.servlet.HttpServletBean#init

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

        // Set bean properties from init parameters.
        //设置初始化参数,比如:context-param
        try {
   
   
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            //把servlet参数设置到BeanWrapper 对象中
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
   
   
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            throw ex;
        }

        // Let subclasses do whatever initialization they like.
        //【重要】初始化ServletBean
        initServletBean();

        if (logger.isDebugEnabled()) {
   
   
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

init方法中调用initServletBean初始化ServletBean,继续跟踪下去,代码来到org.springframework.web.servlet.FrameworkServlet#initServletBean

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

        try {
   
   
         //【重要】这里在初始化WebApplicationContext
            this.webApplicationContext = initWebApplicationContext();
            //空方法,让子类来实现
            initFrameworkServlet();
        }
        catch (ServletException ex) {
   
   
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
   
   
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }

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

FrameworkServlet#initServletBean方法中调用initWebApplicationContext初始化IOC容器对象,在ContextLoaderListener中已经创建了WebApplicationContext , 这里只是做初始化。见:org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

protected WebApplicationContext initWebApplicationContext() {
   
   
        //【重要】从ServletContext中获取WebApplicationContext,也就是通过ContextLoadListener创建的
        // rootContext 根容器,是从ServletContext中拿到的容器对象
WebApplicationContext
        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);
                    }
                    //【重要】刷新上下文,会走到ioc的 refresh();方法
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
   
   
            // No context instance was injected at construction time -> see if one
            // has been registered in the servlet context. If one exists, it is assumed
            // that the parent context (if any) has already been set and that the
            // user has performed any initialization such as setting the context id
            wac = findWebApplicationContext();
        }
        if (wac == null) {
   
   
            //[重要]如果wac 为空,到这里还没有WebApplicationContext就会走
            //FrameworkServlet#createWebApplicationContext,创建一个XmlWebApplicationContext
            //然后执行 wac.setParent(parent); 形成一个父子容器 ,rootContext是针对SpringMVC的容器,wac是针对Spring的容器
            //最后会走容器的Refresh刷新方法刷新容器
            // No context instance is defined for this servlet -> create a local one
            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.
            //[重要]初始化SpringMVC核心组件
            onRefresh(wac);
        }

        if (this.publishContext) {
   
   
            //把context作为ServletContext中的属性
            // Publish the context as a servlet context attribute.
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
   
   
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

initWebApplicationContext方法中从ServletContext中拿到WebApplicationContext作为rootContext根容器,然后会走configureAndRefreshWebApplicationContext方法创建一个新的WebApplicationContext作为子容器形成父子容器,最终调用容器的AbstractApplicationContext#refreshrefresh()刷新容器,这个在Spring源码分析中已经有说道。

刷新完成容器后,会调用 onRefresh(wac)方法; 见org.springframework.web.servlet.DispatcherServlet#onRefresh

/**
     * This implementation calls {@link #initStrategies}.
     */
    @Override
    protected void onRefresh(ApplicationContext context) {
   
   
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
   
   
        //多文件上传的组件 
        initMultipartResolver(context); 
        //初始化本地语言环境 
        initLocaleResolver(context); 
        //初始化模板处理器 
        initThemeResolver(context); 
        //初始化handlerMapping 
        initHandlerMappings(context); 
        //初始化参数适配器 
        initHandlerAdapters(context); 
        //初始化异常拦截器 
        initHandlerExceptionResolvers(context); 
        //初始化视图预处理器 
        initRequestToViewNameTranslator(context); 
        //初始化视图转换器 
        initViewResolvers(context); 
        //FlashMap 管理器 
        initFlashMapManager(context);
    }

initStrategies中初始化了SpringMVC最核心的九大组件

initMultipartResolver(context);

初始化文件上传的组件 ,MultipartResolver作为文件上传解析组件,如果开发中需要使用MultipartResolver需要在xml中配置<bean id="multipartResolver" class="org.Springframework.web.multipart.commons.CommonsMultipartResolver" /> , 这样请求中的multipart属性就会被处理。

private void initMultipartResolver(ApplicationContext context) {
   
   
        try {
   
   
        //从容器中查找Bean,如果在xml配置了这里就能获取到
            this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
   
   
            // Default is no multipart resolver.
            this.multipartResolver = null;
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
                        "': no multipart request handling provided");
            }
        }
    }

initLocaleResolver(context);

初始化本地语言环境 ,Spring国际化支持

private void initLocaleResolver(ApplicationContext context) {
   
   
        try {
   
   
            //从容器中查找localeResolver 
            this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
   
   
            // We need to use the default.
            //使用一个默认的localeResolver 
            this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
                        "': using default [" + this.localeResolver + "]");
            }
        }
    }

initThemeResolver(context);

初始化模板处理器 ,web开发中可以通过 Theme 来控 网页风格,改善应用程序的视觉
效果。

private void initThemeResolver(ApplicationContext context) {
   
   
        try {
   
   
            this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Using ThemeResolver [" + this.themeResolver + "]");
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
   
   
            // We need to use the default.
            this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug(
                        "Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME + "': using default [" +
                                this.themeResolver + "]");
            }
        }
    }

initHandlerMappings(context);

初始化handlerMapping ,HandlerMapping维护了URL和Handler(controller)的映射关系,当客户端发出请求, DispatcherServlet 会将 Request 提交 HandlerMapping,HanlerMapping 根据 Web Application Context 的配置查找相应的 Controller。

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

        if (this.detectAllHandlerMappings) {
   
   
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            //[重要]查找所有的 HandlermApping
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
   
   
                //把handlerMapping存储到 handlerMappings ,一个ArrayList
                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                //按照order排序,
                OrderComparator.sort(this.handlerMappings);
            }
        }
        else {
   
   
            try {
   
   
                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.
        if (this.handlerMappings == null) {
   
   
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }

默认找到所有的HandlerMapping,查找Handler的时候,按照优先级去执行HandlerMapping,如果第一个HandlerMapping不能返回Handler就走第二个HandlerMapping以此类推。

initHandlerAdapters(context);

初始化参数适配器 ,这是个典型的适配器模式的使用,当DispatcherServlet从HandlerMapping中找到对应的Handler后, 会轮询HandlerAdapter模块,查找能够处理当前 HTTP 请求的处理器适配器的实现,然后去执行Handler得到ModelAndView。并把结果返回给DispatcherServlet。

private void initHandlerAdapters(ApplicationContext context) {
   
   
        this.handlerAdapters = null;

        if (this.detectAllHandlerAdapters) {
   
   
            // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
            //查找所有到HandlerAdapters ,
            Map<String, HandlerAdapter> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
            if (!matchingBeans.isEmpty()) {
   
   
            //添加到handlerAdapters,一个ArrayList
                this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
                // We keep HandlerAdapters in sorted order.
                //按照order排序
                OrderComparator.sort(this.handlerAdapters);
            }
        }
        else {
   
   
            try {
   
   
                HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
                this.handlerAdapters = Collections.singletonList(ha);
            }
            catch (NoSuchBeanDefinitionException ex) {
   
   
                // Ignore, we'll add a default HandlerAdapter later.
            }
        }

        // Ensure we have at least some HandlerAdapters, by registering
        // default HandlerAdapters if no other adapters are found.
        if (this.handlerAdapters == null) {
   
   
            this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
            }
        }
    }
  • RequestMappingHandlerAdapter : HandlerAdapter实现的抽象基类,支持HandlerMethod类型的处理程序。
  • HttpRequestHandlerAdapter :HTTP 请求处理器适配器仅仅支持对 HTTP 请求处理 的适配 ,它简单地将 请求对象和响应对象传递给 HTTP 请求处理器HttpRequestHandler,它并不需要返回值 它主要应用在基于 HTTP
    的远程调用的实现上
  • SimpleControllerHandlerAdapter:把http请求适配到控制器controller进行处理。

initHandlerExceptionResolvers(context);

初始化异常拦截器 ,基于 HandlerExceptionResolver 接口的异常处理,使用这种方式只需要实现resolveException方法,该方法根据异常生成一个ModeAndView ,如果该方法返 回了 null ,则 Spring 会继续寻找其他的实现了HandlerExceptionResolver 接口的 bean 。

private void initHandlerExceptionResolvers(ApplicationContext context) {
   
   
        this.handlerExceptionResolvers = null;

        if (this.detectAllHandlerExceptionResolvers) {
   
   
            // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
            //查找所有的异常解析器
            Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
                    .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
            if (!matchingBeans.isEmpty()) {
   
   
                this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
                // We keep HandlerExceptionResolvers in sorted order.
                //按照order优先级排序
                OrderComparator.sort(this.handlerExceptionResolvers);
            }
        }
        else {
   
   
            try {
   
   
                HandlerExceptionResolver her =
                        context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
                this.handlerExceptionResolvers = Collections.singletonList(her);
            }
            catch (NoSuchBeanDefinitionException ex) {
   
   
                // Ignore, no HandlerExceptionResolver is fine too.
            }
        }

        // Ensure we have at least some HandlerExceptionResolvers, by registering
        // default HandlerExceptionResolvers if no other resolvers are found.
        if (this.handlerExceptionResolvers == null) {
   
   
            this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
            }
        }
    }

initRequestToViewNameTranslator(context);

RequestToViewNameTranslator:初始化视图预处理器 ,当我们没有在controller中返回一个视图名的时候,它会按照约定好的规则提供一个默认的视图名,默认使用DefaultRequestToViewNameTranslator,它 会获取到请求的URL,然后把请求的URL改造成一个视图名。

private void initRequestToViewNameTranslator(ApplicationContext context) {
   
   
        try {
   
   
        //视图名解析器
            this.viewNameTranslator =
                    context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Using RequestToViewNameTranslator [" + this.viewNameTranslator + "]");
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
   
   
            // We need to use the default.
            //如果没有配置就使用默认的DefaultRequestToViewNameTranslator
            this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Unable to locate RequestToViewNameTranslator with name '" +
                        REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME + "': using default [" + this.viewNameTranslator +
                        "]");
            }
        }
    }

initViewResolvers(context);

初始化视图解析器,Controller的执行结果被放到ModelAndView中,DispatcherServlet会选择合适的视图解析器解析ModelAndView,ViewResolver中定义了resloverViewName方法,根据viewName选择合适的view实现。View 是用来渲染页面的,也就是说,它会将程序返回 的参数和数据填入模板中,最终生成 html 文件。ViewResolver渲染的过程中会找到渲染所用的模板jsp文件,ftl文件等(使用什么模板来渲染?)和填入参数,生产html文件。
如:InternalResourceViewResolver : 视图解析器,指定前缀后缀,用来根据controller返回的逻辑视图名拼接完整的视图(模板)地址:如 方法返回 "index" ,则视图地址: /jsp/index.jsp

private void initViewResolvers(ApplicationContext context) {
   
   
        this.viewResolvers = null;

        if (this.detectAllViewResolvers) {
   
   
            // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
            //查找所有视图解析器
            Map<String, ViewResolver> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
            if (!matchingBeans.isEmpty()) {
   
   
                this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.values());
                // We keep ViewResolvers in sorted order.
                //视图解析器排序
                OrderComparator.sort(this.viewResolvers);
            }
        }
        else {
   
   
            try {
   
   
                ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
                this.viewResolvers = Collections.singletonList(vr);
            }
            catch (NoSuchBeanDefinitionException ex) {
   
   
                // Ignore, we'll add a default ViewResolver later.
            }
        }

        // Ensure we have at least one ViewResolver, by registering
        // a default ViewResolver if no other resolvers are found.
        if (this.viewResolvers == null) {
   
   
            this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
            }
        }
    }

initFlashMapManager(context);

SpringMVC Flash attributes 提供了一个请求存储性,可供其他请求使用重定向之前暂存(就像存在 sessio
中)以便重定向之后还能使用,并立即删除

private void initFlashMapManager(ApplicationContext context) {
   
   
        try {
   
   
            this.flashMapManager =
                    context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Using FlashMapManager [" + this.flashMapManager + "]");
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
   
   
            // We need to use the default.
            this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Unable to locate FlashMapManager with name '" +
                        FLASH_MAP_MANAGER_BEAN_NAME + "': using default [" + this.flashMapManager + "]");
            }
        }
    }

下面这个则是更细一点的执行原理流程图
在这里插入图片描述
如果对你有所帮助,请使劲三连。请使劲三连。请使劲三连。请使劲三连。请使劲三连。

相关文章
|
缓存 Java Spring
Spring中Bean创建过程之源码分析
Spring中Bean创建过程之源码分析
80 0
|
6月前
|
Java 应用服务中间件 数据库连接
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
65 0
|
Java Spring
spring bean初始化过程
从这个简单的代码衍生,使用AnnotationConfigApplicationContext看一下spring bean的初始化过程
177 0
spring bean初始化过程
|
存储 前端开发 Java
二.SpringMVC源码剖析-SpringMVC执行流程
上一篇文章写得是SpringMVC组件初始化,这篇文章我们来探讨一下SpringMVC的执行流程
|
JSON 前端开发 Java
SpringMVC源码剖析之SpringMVC工作流程
HandlerMapping 是⽤来查找 Handler 的,也就是处理器,具体的表现形式可以是类,也可以是⽅法。 ⽐如,标注了@RequestMapping的每个⽅法都可以看成是⼀个Handler。Handler负责具 体实际的请求处理,在请求到达后,HandlerMapping 的作⽤便是找到请求相应的处理器Handler 和 Interceptor.
119 0
|
Java 容器 Spring
SpringMVC学习(十一):注解配置SpringMVC
使用配置类和注解代替web.xml和SpringMVC配置文件的功能
140 0
|
XML Java 数据格式
Spring-初始化源码分析
Spring-初始化源码分析
Spring-初始化源码分析
|
XML 设计模式 前端开发
springMvc43-springMVC运行原理
springMvc43-springMVC运行原理
108 0
springMvc43-springMVC运行原理
|
存储 缓存 搜索推荐
Spring 源码学习(四) bean 的加载(上)
既然我们 Spring 辛辛苦苦将 bean 进行了注册,当然需要拿出来进行使用,在使用之前还需要经过一个步骤,就是 bean 的加载。 在第一篇笔记提到了,完成 bean 注册到 beanDefinitionMap 注册表后,还调用了很多后处理器的方法,其中有一个方法 finishBeanFactoryInitialization(),注释上面写着 Instantiateall remaining(non-lazy-init)singletons,意味着非延迟加载的类,将在这一步实例化,完成类的加载。 而我们使用到 context.getBean("beanName")方法,如果对应的
Spring 源码学习(四) bean 的加载(上)
|
Java Spring
Spring整合SpringMVC时避免Spring加载两次bean的配置方法
Spring整合SpringMVC时避免Spring加载两次bean的配置方法