`

Spring MVC中的IoC容器初始化

阅读更多

Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext,使得拥有web功能。那么,Spring MVC是如何在web环境中创建IoC容器呢?web环境中的IoC容器的结构又是什么结构呢?web环境中,spring IoC容器是怎么启动呢?

      先看一下WebApplicationContext是如何扩展ApplicationContext来添加对Web环境的支持的。WebApplicationContext接口定义如下:

 

public interface WebApplicationContext extends ApplicationContext {  
    //根上下文在ServletContext中的名称  
        String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";  
        //取得web容器的ServletContext  
    ServletContext getServletContext();  
}  

 

      对于web容器中创建IoC容器的过程,我们从web.xml配置文件讲起。看一下Spring MVC的web.xml中的相关配置:

    

<context-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>/WEB-INF/applicationContext.xml</param-value>  
    </context-param>  
      
    <listener>  
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    </listener>  
  
    <!-- Handles all requests into the application -->  
    <servlet>  
        <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>  
                /WEB-INF/spring/*.xml  
            </param-value>  
        </init-param>  
        <load-on-startup>1</load-on-startup>  
    </servlet>  
          
    <!-- Maps all /app requests to the DispatcherServlet for handling -->  
    <servlet-mapping>  
        <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>  
        <url-pattern>/app/*</url-pattern>  
    </servlet-mapping>  

 

      在web.xml配置文件中,有两个主要的配置:ContextLoaderListener和DispatcherServlet。同样的关于spring配置文件的相关配置也有两部分:context-param和DispatcherServlet中的init-param。那么,这两部分的配置有什么区别呢?它们都担任什么样的职责呢?

      在Spring MVC中,Spring Context是以父子的继承结构存在的。Web环境中存在一个ROOT Context,这个Context是整个应用的根上下文,是其他context的双亲Context。同时Spring MVC也对应的持有一个独立的Context,它是ROOT Context的子上下文。

      对于这样的Context结构在Spring MVC中是如何实现的呢?下面就先从ROOT Context入手,ROOT Context是在ContextLoaderListener中配置的,ContextLoaderListener读取context-param中的contextConfigLocation指定的配置文件,创建ROOT Context。下面看一下ContextLoaderListener中创建context的源码:

 

/** 
     * Initialize Spring's web application context for the given servlet context, 
     * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and 
     * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. 
     * @param servletContext current servlet context 
     * @return the new WebApplicationContext 
     * @see #CONTEXT_CLASS_PARAM 
     * @see #CONFIG_LOCATION_PARAM 
     */  
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {  
                //PS : ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.class.getName() + ".ROOT" 根上下文的名称  
                  //PS : 默认情况下,配置文件的位置和名称是: DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"  
                  //在整个web应用中,只能有一个根上下文  
        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!");  
        }  
  
        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 {  
            // Determine parent for root web application context, if any.  
            ApplicationContext parent = loadParentContext(servletContext);  
  
            // Store context in local instance variable, to guarantee that  
            // it is available on ServletContext shutdown.  
                        // 在这里执行了创建WebApplicationContext的操作  
            this.context = createWebApplicationContext(servletContext, parent);  
  
                        //PS: 将根上下文放置在servletContext中  
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  
  
            ClassLoader ccl = Thread.currentThread().getContextClassLoader();  
            if (ccl == ContextLoader.class.getClassLoader()) {  
                currentContext = this.context;  
            }  
            else if (ccl != null) {  
                currentContextPerThread.put(ccl, this.context);  
            }  
  
            if (logger.isDebugEnabled()) {  
                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +  
                        WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");  
            }  
            if (logger.isInfoEnabled()) {  
                long elapsedTime = System.currentTimeMillis() - startTime;  
                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");  
            }  
  
            return this.context;  
        }  
        catch (RuntimeException ex) {  
            logger.error("Context initialization failed", ex);  
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);  
            throw ex;  
        }  
        catch (Error err) {  
            logger.error("Context initialization failed", err);  
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);  
            throw err;  
        }  
    }  

 

    再看一下WebApplicationContext对象是如何创建的:

 

protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {  
                //根据web.xml中的配置决定使用何种WebApplicationContext。默认情况下使用XmlWebApplicationContext  
                //web.xml中相关的配置context-param的名称“contextClass”  
        Class<?> contextClass = determineContextClass(sc);  
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {  
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +  
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");  
        }  
  
                //实例化WebApplicationContext的实现类  
        ConfigurableWebApplicationContext wac =  
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
  
        // Assign the best possible id value.  
        if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {  
            // Servlet <= 2.4: resort to name specified in web.xml, if any.  
            String servletContextName = sc.getServletContextName();  
            if (servletContextName != null) {  
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName);  
            }  
            else {  
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX);  
            }  
        }  
        else {  
            // Servlet 2.5's getContextPath available!  
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + sc.getContextPath());  
        }  
  
        wac.setParent(parent);  
  
        wac.setServletContext(sc);  
                //设置spring的配置文件  
        wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));  
        customizeContext(sc, wac);  
                //spring容器初始化  
        wac.refresh();  
        return wac;  
    }  

 

       以上是web容器中根上下文的加载与初始化,下面介绍一下Spring MVC对应的上下文是如何加载的。

     

       Spring MVC中核心的类是DispatcherServlet,在这个类中完成Spring context的加载与创建,并且能够根据Spring Context的内容将请求分发给各个Controller类。DispatcherServlet继承自HttpServlet,关于Spring Context的配置文件加载和创建是在init()方法中进行的,主要的调用顺序是init-->initServletBean-->initWebApplicationContext。

       先来看一下initWebApplicationContext的实现

 

 

/** 
     * Initialize and publish the WebApplicationContext for this servlet. 
     * <p>Delegates to {@link #createWebApplicationContext} for actual creation 
     * of the context. Can be overridden in subclasses. 
     * @return the WebApplicationContext instance 
     * @see #setContextClass 
     * @see #setContextConfigLocation 
     */  
    protected WebApplicationContext initWebApplicationContext() {  
                //先从web容器的ServletContext中查找WebApplicationContext  
        WebApplicationContext wac = findWebApplicationContext();  
        if (wac == null) {  
            // No fixed context defined for this servlet - create a local one.  
                        //从ServletContext中取得根上下文  
            WebApplicationContext parent =  
                    WebApplicationContextUtils.getWebApplicationContext(getServletContext());  
                        //创建Spring MVC的上下文,并将根上下文作为起双亲上下文  
            wac = createWebApplicationContext(parent);  
        }  
  
        if (!this.refreshEventReceived) {  
            // Apparently not a ConfigurableApplicationContext with refresh support:  
            // triggering initial onRefresh manually here.  
            onRefresh(wac);  
        }  
  
        if (this.publishContext) {  
            // Publish the context as a servlet context attribute.  
                        // 取得context在ServletContext中的名称  
            String attrName = getServletContextAttributeName();  
                        //将Spring MVC的Context放置到ServletContext中  
            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方法的调用,创建了DispatcherServlet对应的context,并将其放置到ServletContext中,这样就完成了在web容器中构建Spring IoC容器的过程。

 

        最后,在分别给出ContextLoaderListener和DispatcherServlet构建context的时序。

 

        ContextLoaderListener构建Root Context时序图:

 ContextLoaderListener

         DispatcherServlet创建context时序图:

 DispatcherServlet创建context时序图

分享到:
评论

相关推荐

    00-myspring.war

    初始化阶段:重写Servlet的init方法,实现加载配置文件,读取配置信息, 扫描所有相关的类, 初始化相关类的实例并保存到IOC容器, 从IOC容器取出对应的实例给字段赋值,即依赖注入, 最后将url和Method进行关联。...

    Spring MVC之DispatcherServlet详解_动力节点Java学院整理

    DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。 具体请参考第二章的图2-1。  ...

    spring.doc

    Spring容器IOC和di的整个启动过程: 38 3.8 spring中的继承 38 拓展spring为类中的属性赋值: 40 小结: 47 面向接口编程: 47 4 面向切面编程 52 4.1 代理模式 52 代理模式拓展: 52 4.1.1 JDK动态代理 58 JDK动态...

    Spring-Reference_zh_CN(Spring中文参考手册)

    3.3.5. 延迟初始化bean 3.3.6. 自动装配(autowire)协作者 3.3.6.1. 设置Bean使自动装配失效 3.3.7. 依赖检查 3.3.8. 方法注入 3.3.8.1. Lookup方法注入 3.3.8.2. 自定义方法的替代方案 3.4. bean的作用域 3.4.1. ...

    spring-framework-3.1.0.RELEASE.zip

    你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。 面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务...

    Spring中文帮助文档

    12.2.2. 在Spring容器中创建 SessionFactory 12.2.3. The HibernateTemplate 12.2.4. 不使用回调的基于Spring的DAO实现 12.2.5. 基于Hibernate3的原生API实现DAO 12.2.6. 编程式的事务划分 12.2.7. 声明式的...

    Spring 3 Reference中文

    4.6.1.6 在非Web 应用中,优雅地关闭Spring IoC 容器 74 4.6.2 ApplicationContextAware 和BeanNameAware . 75 4.6.3 其它Aware 接口 75 4.7 Bean 定义的继承. 77 4.8 容器扩展点. 78 4.8.1...

    spring源代码解析

    简单的说,在web容器中,通过ServletContext为Spring的IOC容器提供宿主环境,对应的建立起一个IOC容器的体系。其中,首先需要建立的是根上下文,这个上下文持有的对象可以有业务对象,数据存取对象,资源,事物管理...

    Spring面试题

    容器(在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。 在典型的 IOC 场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法。下表列出了 IOC 的一个实现模式。 Spring ...

    Spring 2.0 开发参考手册

    3.3.5. 延迟初始化bean 3.3.6. 自动装配(autowire)协作者 3.3.7. 依赖检查 3.3.8. 方法注入 3.4. bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用域 3.4.3. 其他作用域 3.4.4. 自定义作用域 ...

    Spring攻略(第二版 中文高清版).part1

    1.2 配置Spring IoC容器中的Bean 4 1.2.1 问题 4 1.2.2 解决方案 4 1.2.3 工作原理 4 1.3 调用构造程序创建Bean 14 1.3.1 问题 14 1.3.2 解决方案 14 1.3.3 工作原理 14 1.4 解决构造程序歧义 17 ...

    Spring API

    12.2.2. 在Spring容器中创建 SessionFactory 12.2.3. The HibernateTemplate 12.2.4. 不使用回调的基于Spring的DAO实现 12.2.5. 基于Hibernate3的原生API实现DAO 12.2.6. 编程式的事务划分 12.2.7. 声明式的...

    spring chm文档

    3.3.5. 延迟初始化bean 3.3.6. 自动装配(autowire)协作者 3.3.7. 依赖检查 3.3.8. 方法注入 3.4. bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用域 3.4.3. 其他作用域 3.4.4. 自定义作用域 ...

    Spring攻略(第二版 中文高清版).part2

    1.2 配置Spring IoC容器中的Bean 4 1.2.1 问题 4 1.2.2 解决方案 4 1.2.3 工作原理 4 1.3 调用构造程序创建Bean 14 1.3.1 问题 14 1.3.2 解决方案 14 1.3.3 工作原理 14 1.4 解决构造程序歧义 17 ...

    xmljava系统源码-springframework-source-5.1.x:这是一个注释版的Spring源码分析工程,版本为5.1.x,

    为Spring提供了基础的Web功能支持,主要建立于核心容器之上,通过Servlet或者Listeners来初始化IOC容器; spring-webmvc: 主要提供对SpringMVC的支持,例如SpringMVC中的DispatcherServlet就是该模块中提供的; ...

    spring-demo:手写实现Spring核心功能

    项目入口项目的入口为DispatcherSerlvet的init()方法中,在Servlet 的 init 方法初始化了IOC容器和Spring MVC所依赖的组件项目搭建用户配置application.properties配置application.properties作为配置文件,配置所...

    struts spring hibernate整合框架

    Spring IOC : 将程序中容易产生耦合的黏贴代码( dao.setDriver() ) 从程序中提取出去, 配置到配置文件中,由 工厂 根据配置文件创建,并初始化对象。这样,在代码 中就可以彻底利用接口进行编程.

    spring3.1中文参考文档

    2.5.3 控制反转(IoC)容器 ................................................................................................................ 25 2.5.3.1 基于Java的bean元数据.................................

    基于maven项目的SSM框架与layu前端框架的代码

    在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter...

    java-advance:java进阶,包含 常用设计模式、线程和并发、spring核心代码、mybatis核心代码、springboot2.0、springcloud G版本、docker的使用、各类面试题

    java进阶源码分析专题常用设计模式线程与并发锁的使用深度理解synchronized、volatile、cas手写ASQSpring5IOC容器设计原理及高级特性AOP设计原理FactoryBean与BeanFactorySpring事务处理机制Spring JDK动态代理...

Global site tag (gtag.js) - Google Analytics