最新公告
  • 欢迎您光临波比源码,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • spring源码研究之IoC容器在web容器中初始化过程

    前段时间在公司做了1个项目,项目用了spring框架实现,WEB容器是Tomct 5,虽然说把项目做完了,但是1直对spring的IoC容器在web容器如何启动和起作用的其实不清楚。所以就抽时间看1下spring的源代码,借此了解它的原理。

        我们知道,对使用Spring的web利用,不必手动创建Spring容器,而是通过配置文件,声明式的创建Spring容器。因此在Web利用中创建Spring容器有以下两种方式:

        1. 直接在web.xml文件中配置创建Spring容器。

        2. 利用第3方MVC框架的扩大点,创建Spring容器。

        其实第1种方式是更加常见。为了让Spring容器随Web利用的启动而启动,有以下两种方式:

        1. 利用ServletContextListener实现。

        2. 利用load-on-startup Servlet实现。

        Spring提供ServletContextListener的1个实现类ContextLoaderListener,该类可以作为Listener 使用,它会在创建时自动查找WEB-INF下的applicationContext.xml文件,因此,如果只有1个配置文件,并且文件名为applicationContext.xml,则只需在web.xml文件中增加以下配置片断就能够了

    <listener>
    <listener-class>
    org.springframework.web.context.ContextLoaderListener
    </listener-class>
    </listener>

    如果有多个配置文件需要载入,则斟酌使用<context-param…>元夙来肯定配置文件的文件名。ContextLoaderListener加载时,会查找名为contentConfigLocation的初始化参数。因此,配置<context-param…>时就指定参数名为contextConfigLocation。

        带多个配置文件的web.xml文件以下:

    <context-param>
    <param-name>contextLoaderListener</param-name>
    <param-value>
    WEB-INF/*.xml, classpath:spring/*.xml
    </param-value>
    </context-param>

    <listener>
    <listener-class>
    org.springframework.web.context.ContextLoaderListener
    </listener-class>
    </listener>

      多个配置文件之间用“,”隔开。

     

        下面我们来看它的具体实现进程是怎样的,首先我们从ContextLoaderListener入手,它的代码以下:

    public class ContextLoaderListener implements ServletContextListener
    {

    private ContextLoader contextLoader;

    /**
    * 这个方法就是用来初始化web application context的
    */
    public void contextInitialized(ServletContextEvent event)
    {
    this.contextLoader = createContextLoader();
    this.contextLoader.initWebApplicationContext(event.getServletContext());
    }

    /**
    * 创建1个contextLoader.
    * @return the new ContextLoader
    */
    protected ContextLoader createContextLoader()
    {
    return new ContextLoader();
    }
    …………….

    }

    我们看到初始化web application context的时候,首先通过new ContextLoader()创建1个contextLoader,

       new ContextLoader()具体做了甚么事呢?ContextLoader的代码片断:

    static {
    try {
    // 这里创建1个ClassPathResource对象,载入ContextLoader.properties,用于创建对应的ApplicationContext容器
    // 这个文件跟ContextLoader类在同1个目录下,文件内容如:
    // org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
    // 如此说来,spring默许初始化的是XmlWebApplicationContext
    ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
    // 得到1个Properties对象,后面根据类名来创建ApplicationContext容器
    defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }catch (IOException ex) {
    throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
    }
    }

    代码注释里面已说得很清楚了,很容易理解吧?嘿嘿……

        再下来我们再看1下initWebApplicationContext方法的实现进程:

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
    throws IllegalStateException, BeansException {

    // 从servletContext中获得ApplicationContext容器;如果已存在,则提示初始化容器失败,检查web.xml文件中是不是定义有多个容器加载器
    // ServletContext接口的简述:public interface ServletContext
    // 定义了1系列方法用于与相应的servlet容器通讯,比如:取得文件的MIME类型,分派要求,或是向日志文件写日志等。
    // 每个web-app只能有1个ServletContext,web-app可以是1个放置有web application 文件的文件夹,也能够是1个.war的文件。
    // ServletContext对象包括在ServletConfig对象当中,ServletConfig对象在servlet初始化时提供servlet对象。

    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
    throw new IllegalStateException(
    "Cannot initialize context because there is already a root application context present – " +
    "check whether you have multiple ContextLoader* definitions in your web.xml!");
    }

    servletContext.log("Initializing Spring root WebApplicationContext");
    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.
    // 创建ApplicationContext容器
    this.context = createWebApplicationContext(servletContext, parent);
    // 把容器放入到servletContext中
    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, 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;
    }
    }

    从上面的代码可以看出,我们创建好的applicationContext容器会放在servletContext中。servletContext是甚么 呢?

    在web容器中,通过ServletContext为Spring的IOC容器提供宿主环境,对应的建立起1个IOC容器的体系。其中,首先需要建立的是根上下文,这个上下文持有的对象可以有业务对象,数据存取对象,资源,事物管理器等各种中间层对象。在这个上下文的基础上,和web MVC相干还会有1个上下文来保存控制器之类的MVC对象,这样就构成了1个层次化的上下文结构。

    从initWebApplicationContext中可以看到真正创建applicationContext容器是由createWebApplicationContext方法来实现的,它的代码以下:

    protected WebApplicationContext createWebApplicationContext(
    ServletContext servletContext, ApplicationContext parent) throws BeansException
    {
    // 首先决定要创建的applicationContext容器的类
    Class contextClass = determineContextClass(servletContext);
    // 如果获得到的类不是ConfigurableWebApplicationContext类型的,则创建容器失败,所以这里创建的容器必须是ConfigurableWebApplicationContext类型的
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass))
    {
    throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }

    // 实例化spring容器
    ConfigurableWebApplicationContext wac =
    (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    wac.setParent(parent);
    wac.setServletContext(servletContext);
    // 获得contextConfigLocation初始化参数,该参数记录的是需要载入的多个配置文件(即定义bean的配置文件)
    String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
    if (configLocation != null)
    {
    wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,
    ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
    }

    wac.refresh();
    return wac;
    }

    createWebApplicationContext方法实现步骤为:

        1. 首先决定要创建的applicationContext容器的类
        2. 实例化applicationContext容器

        但它是如何决定要创建的容器类呢?我们看1下determineContextClass方法:

      

    protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException
    {
    // 从web.xml中获得需要初始化的容器的类名
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    // 如果获得到的类名不为空,则创建该容器的Class对象
    if (contextClassName != null)
    {
    try {
    return ClassUtils.forName(contextClassName);
    }
    catch (ClassNotFoundException ex) {
    throw new ApplicationContextException(
    "Failed to load custom context class [" + contextClassName + "]", ex);
    }
    }
    // 否则创建默许的容器的Class对象,即:org.springframework.web.context.support.XmlWebApplicationContext
    // 在创建ContextLoader时,defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);这句代码已准备好默许的容器类
    else
    {
    contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
    try
    {
    return ClassUtils.forName(contextClassName);
    }
    catch (ClassNotFoundException ex)
    {
    throw new ApplicationContextException(
    "Failed to load default context class [" + contextClassName + "]", ex);
    }
    }
    }

    该方法首先判断从web.xml文件的初始化参数CONTEXT_CLASS_PARAM(的定义为public static final String CONTEXT_CLASS_PARAM = "contextClass";)获得的的类名是不是存在,如果存在,则容器的Class;否则返回默许的Class。如何获得默许的容器Class,注意看创建contextLoader时的代码注释就知道了。

        由此看来,spring不但有默许的applicationContext的容器类,还允许我们自定义applicationContext容器类,不过Spring不建义我们自定义applicationContext容器类。

      2、利用load-on-startup Servlet实现。

    <servlet>
    <servlet-name>springServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>springServlet</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>

     

       好了,这就是spring的IoC容器在web容器如何启动和起作用的全部进程。仔细的朋友可以看出创建applicationContext容器的同时会初始化配置文件中定义的bean类,createWebApplicationContext方法中的wac.refresh();这段代码就是用来初始化配置文件中定义的bean类的。它具体的实现进程现在还没完全弄清楚,等弄清楚了再跟大家分享!

    波比源码 – 精品源码模版分享 | www.bobi11.com
    1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
    2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
    3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
    4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
    5. 如有链接无法下载、失效或广告,请联系管理员处理!
    6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
    7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!

    波比源码 » spring源码研究之IoC容器在web容器中初始化过程

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    波比源码
    一个高级程序员模板开发平台
    升级波友尊享更多特权立即升级