Spring源码阅读(九)—SpringMVC的初始化

SpringMVC初始化详解:从Web容器到DispatcherServlet
本文详细梳理了SpringMVC在Web应用启动时的初始化过程,包括由ContextLoaderListener初始化ApplicationContext,以及DispatcherServlet的初始化。ContextLoaderListener负责结合Spring的IOC和AOP功能与Web环境,创建并存储WebApplicationContext。而DispatcherServlet则在Web容器启动时初始化,通过initServletBean()方法补充和初始化ContextLoaderListener创建的ApplicationContext,确保SpringMVC的核心逻辑得以执行。

Spring源码阅读(九)—SpringMVC的初始化

本文主要整理了Web应用启动时具体的初始化流程,包括Spring上下文的的初始化和SpringMVC的初始化

个人主页:tuzhenyu’s page
原文地址:Spring源码阅读(九)—SpringMVC的初始化

(1) SpringMVC的初始化

SpringMVC的初始化是从Web容器如Tomcat,Jetty等启动时加载解析web.xml文件开始的,初始化只要包括两部分:

  • ContextLoaderListener初始化

  • DispatcherServlet的初始化

(2) ContextLoaderListener初始化

ContextLoaderListener初始化的主要功能是将在Web应用启动时,自动装配ApplicationContext上下文环境,将Spring的IOC,AOP等功能与Web环境结合起来;每个Web应用都有一个ServletContext与之相关联作为整个Web应用的全局变量的存放地,ContextLoaderListener的核心逻辑就是初始化WebApplicationContext实例并将之放在servletContext中.

  • 使用ContextLoaderListener初始化之前需要设定Spring的配置文件地址
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
  • 注册ContextLoaderListener监听器,在Web容器启动时会创建并初始化该监听器
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
  • ContextLoaderListener监听器实现了ServletContextListener接口对ServletContext进行监听,当ServletContext创建后会调用其contextInitialized()方法对ServletContext进行初始化;在contextInitialized()方法中会调用initWebApplicationContext()方法初始化WebApplicationContext上下文环境.
public void contextInitialized(ServletContextEvent event) {
    this.initWebApplicationContext(event.getServletContext());
}
  • initWebApplicationContext()方法调用createWebApplicationContext()方法创建上下文环境对象,调用

configureAndRefreshWebApplicationContext()方法对上下文进行初始化(refresh),并将其记录在servletContext中

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!");
    } else {
        Log logger = LogFactory.getLog(ContextLoader.class);
        servletContext.log("Initializing Spring root WebApplicationContext");
        if(logger.isInfoEnabled()) {
            logger.info("Root WebApplicationContext: initialization started");
        }

        long startTime = System.currentTimeMillis();

        try {
            if(this.context == null) {
                this.context = this.createWebApplicationContext(servletContext);
            }

            if(this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext err = (ConfigurableWebApplicationContext)this.context;
                if(!err.isActive()) {
                    if(err.getParent() == null) {
                        ApplicationContext elapsedTime = this.loadParentContext(servletContext);
                        err.setParent(elapsedTime);
                    }

                    this.configureAndRefreshWebApplicationContext(err, servletContext);
                }
            }

            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
            ClassLoader err1 = Thread.currentThread().getContextClassLoader();
            if(err1 == ContextLoader.class.getClassLoader()) {
                currentContext = this.context;
            } else if(err1 != null) {
                currentContextPerThread.put(err1, this.context);
            }

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

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

            return this.context;
        } catch (RuntimeException var8) {
            logger.error("Context initialization failed", var8);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
            throw var8;
        } catch (Error var9) {
            logger.error("Context initialization failed", var9);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
            throw var9;
        }
    }
}
  • ServletContextListener创建ApplicationContext上下文环境主要包括以下几步:

    • 在创建的过程中,会提取将要实现的WebApplicationContext接口的实现类,再根据这个实现类通过forName()反射的方式进行实例的创建;

    • 将配置的contextConfigLocation参数(即Spring配置文件applicationContext.xml)传入ApplicationContext对象并调用refresh()方法刷新Spring容器,完成Spring容器的创建和启动;

    • 将创建好的ApplicationContext放入ServletContext中;

(3) DispatcherServlet的初始化

DispatcherServlet实现了Servlet接口,并且在web.xml配置中load-on-startup为1则DispatcherServlet的实例化和初始化会在Web容器启动时候进行.DispatcherServlet初始化会调用init()方法

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

    HttpServletBean.ServletConfigPropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
    if(!pvs.isEmpty()) {
        try {
            BeanWrapper ex = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ServletContextResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
            ex.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
            this.initBeanWrapper(ex);
            ex.setPropertyValues(pvs, true);
        } catch (BeansException var4) {
            if(this.logger.isErrorEnabled()) {
                this.logger.error("Failed to set bean properties on servlet \'" + this.getServletName() + "\'", var4);
            }

            throw var4;
        }
    }

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

}

DispatcherServlet的初始化主要是将当前的servlet类型的实例转换成BeanWrapper类型的实例,并使用Spring提供的注入功能进行相应属性的注入.同时对servletBean进行初始化.

  • initServletBean()方法主要是调用initWebApplication()方法对ContextLoadListener创建的ApplicationContext上下文环境进行进一步的补充.
protected final void initServletBean() throws ServletException {
    this.getServletContext().log("Initializing Spring FrameworkServlet \'" + this.getServletName() + "\'");
    if(this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization started");
    }

    long startTime = System.currentTimeMillis();

    try {
        this.webApplicationContext = this.initWebApplicationContext();
        this.initFrameworkServlet();
    } catch (ServletException var5) {
        this.logger.error("Context initialization failed", var5);
        throw var5;
    } catch (RuntimeException var6) {
        this.logger.error("Context initialization failed", var6);
        throw var6;
    }

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

}
  • initWebApplicationContext()方法主要是创建或刷新WebApplicationContext实例并对servlet功能所使用的变量进行初始化

    • 判断创建DispatcherServlet时候是否通过构造器传入了创建好的WebApplicationContext实例,默认构造器未传入

    • 判断servletContext中是否存在已经创建好的WebApplicationContext实例,一般是由ServletContextListener创建并放入servletContext中.

    • 如果以上两种都没有找到创建好的WebApplicationContext实例,则从头创建并进行初始化

    • 调用initStrategies()方法对servlet功能所使用的变量进行初始化

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
    WebApplicationContext wac = null;
    if(this.webApplicationContext != null) {
        wac = this.webApplicationContext;
        if(wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac;
            if(!attrName.isActive()) {
                if(attrName.getParent() == null) {
                    attrName.setParent(rootContext);
                }

                this.configureAndRefreshWebApplicationContext(attrName);
            }
        }
    }

    if(wac == null) {
        wac = this.findWebApplicationContext();
    }

    if(wac == null) {
        wac = this.createWebApplicationContext(rootContext);
    }

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

    if(this.publishContext) {
        String attrName1 = this.getServletContextAttributeName();
        this.getServletContext().setAttribute(attrName1, wac);
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]");
        }
    }

    return wac;
}
  • 调用initStrategies()方法对servlet功能所使用的变量进行初始化,这个函数才是SpringMVC核心逻辑的初始化所在
protected void initStrategies(ApplicationContext context) {
    this.initMultipartResolver(context);
    this.initLocaleResolver(context);
    this.initThemeResolver(context);
    this.initHandlerMappings(context);
    this.initHandlerAdapters(context);
    this.initHandlerExceptionResolvers(context);
    this.initRequestToViewNameTranslator(context);
    this.initViewResolvers(context);
    this.initFlashMapManager(context);
}

(4) 结论

SpringMVC的初始化主要包括两部分:ServletContextListener监听器初始化WebApplicationContext将Spring与Web应用结合起来,DispatcherServlet初始化判断是否WebApplicationContext已经被创建如果没有则重新创建,如果有则进行相应的属性注入,同时也会对servlet功能所使用的变量进行初始化.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值