`
gstarwd
  • 浏览: 1525070 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

读Spring源代码之按图索骥(一)Context创建与配置文件加载

阅读更多

Spring 和 Struts在web.xml中增加的配置:

   1:
 <!-- spring的配置 -->
   2:
 <
context-param
>
   3:
     <
param-name
>
contextConfigLocation</
param-name
>
   4:
     <
param-value
>
classpath:/SpringContext/applicationContext-web.xml</
param-value
>
   5:
 </
context-param
>
    
   7:
  
   8:
 <
listener
>
   9:
     <
listener-class
>
org.springframework.web.context.ContextLoaderListener
  10:
     </
listener-class
>
  11:
 </
listener
>
  12:
  
  13:
 <
filter
>
  14:
     <
filter-name
>
struts2</
filter-name
>
  15:
     <
filter-class
>
org.apache.struts2.dispatcher.FilterDispatcher</
filter-class
>
  17:
     <
init-param
>
  18:
         <
param-name
>
config</
param-name
>
  19:
         <
param-value
>
struts-default.xml,struts-plugin.xml,struts/struts.xml</
param-value
>
  20:
     </
init-param
>
  21:
 </
filter
>
  22:
  
  23:
 <
filter-mapping
>
  24:
     <
filter-name
>
struts2</
filter-name
>
  25:
     <
url-pattern
>
*.do</
url-pattern
>
  26:
 </
filter-mapping
>

 

第一个tag定义的是spring的配置文件地址到环境参数(context parameter)

第二个tag定义一个listener为org.springframework.web.context.ContextLoaderListener ,这里相当于j2ee容器给我们提供的main函数的切入点,可以让我们做一些系统初始化的工作,需要实现的类是:javax.servlet.ServletContextListener

第三个tag则定义了struts2的一个filter。Filter则是对每次请求(可以通过filter-mapping指定)做过滤处理,请 求首先请过filter链的处理,然后再到HttpServlet的init方法。对应的类是:javax.servlet.Filter。上面先配置了 一个filter,对应的类是org.apache.struts2.dispatcher.FilterDispatcher,参数则是struts的 配置文件位置

第四个参数定义了filter怎样行为,显然它对.do为后缀的请求应用struts2这个名称的filter

这里需要首先搞清楚servlet规范中什么是listener?
 
详细请参见 Servlet Listener和Filter

 

查看ContextLoaderListener可知,它正好继承了javax.servlet.ServletContextListener ,用于监听javax.servlet.ServletContextEvent事件

/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.springframework.web.context;
 
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
 
/**
 * Bootstrap listener to start up Spring's root {@link WebApplicationContext}.
 * Simply delegates to {@link ContextLoader}.
 *
 * <p>This listener should be registered after
 * {@link org.springframework.web.util.Log4jConfigListener}
 * in <code>web.xml</code>, if the latter is used.
 *
 * @author Juergen Hoeller
 * @since 17.02.2003
 * @see ContextLoaderServlet
 * @see org.springframework.web.util.Log4jConfigListener
 */
public
 class
 ContextLoaderListener implements ServletContextListener {
 
    private
 ContextLoader contextLoader;
 
 
    /**
     * Initialize the root web application context.
     */
    public
 void
 contextInitialized(ServletContextEvent event
) {
        this
.contextLoader = createContextLoader();
        this
.contextLoader.initWebApplicationContext(event
.getServletContext());
    }
 
    /**
     * Create the ContextLoader to use. Can be overridden in subclasses.
     * @return the new ContextLoader
     */
    protected
 ContextLoader createContextLoader() {
        return
 new
 ContextLoader();
    }
 
    /**
     * Return the ContextLoader used by this listener.
     * @return the current ContextLoader
     */
    public
 ContextLoader getContextLoader() {
        return
 this
.contextLoader;
    }
 
 
    /**
     * Close the root web application context.
     */
    public
 void
 contextDestroyed(ServletContextEvent event
) {
        if
 (this
.contextLoader != null
) {
            this
.contextLoader.closeWebApplicationContext(event
.getServletContext());
        }
    }
 
}

此类implement了ServletContextListener的两个方法:

public
 void
 contextInitialized(ServletContextEvent event
);
public
 void
 contextDestroyed(ServletContextEvent event
);

分别做context的初始化和销毁

另外提供了一个protected方法:protected ContextLoader createContextLoader()  用于创建真正做事情的代理类CotextLoader

和一个public方法:public ContextLoader getContextLoader();

 

可见,这个Listener类直接将工作代理 给了ContextLoader类了

 

____________________________________________________________________________________________________________________________

我们按图索骥,下面再分析org.springframework.web.context.ContextLoader

ContextLoader里面有个私有成员:private WebApplicationContext context

此变量提供了create方法:

createWebApplicationContext(ServletContext servletContext, ApplicationContext parent)

因为Spring提供了多种WebApplicationContext类,所以需要一个方法来决定使用哪个WebApplicationContextContext类

protected
 Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
        if
 (contextClassName != null
) {
            try
 {
                return
 ClassUtils.forName(contextClassName);
            }
            catch
 (ClassNotFoundException ex) {
                throw
 new
 ApplicationContextException(
                        "Failed to load custom context class ["
 + contextClassName + "]"
, ex);
            }
        }
        else
 {
            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);
            }
        }
    }

 

public static final String CONTEXT_CLASS_PARAM = "contextClass";

所以如果在web.xml中的<context-param> </context-param>中定义了参数contextClass,那么直接就决定了用此Context类

否则,就应用缺省策略来决定使用哪个Context类。

缺省策略如下:

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

实际上,缺省策略从org\springframework\web\context\ContextLoader.properties 文件中取得属性org.springframework.web.context.WebApplicationContext

我们看看ContextLoader.properties文件的内容:

# Default WebApplicationContext implementation class
 for
 ContextLoader.
# Used as
 fallback when no explicit
 context implementation has been specified as
 context-param.
# Not meant to be customized by application developers.
 
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

显然,缺省的Context类就是 XmlWebApplicationContext类

!!!真累啊,Spring...

 

OK,总算知道用哪个Context类了,那么现在到了create这个Context实例的时候了

protected
 WebApplicationContext createWebApplicationContext(
            ServletContext servletContext, ApplicationContext parent) throws BeansException {
 
        Class contextClass = determineContextClass(servletContext);
        if
 (!ConfigurableWebApplicationContext.class
.isAssignableFrom(contextClass)) {
            throw
 new
 ApplicationContextException("Custom context class ["
 + contextClass.getName() +
                    "] is not of type ["
 + ConfigurableWebApplicationContext.class
.getName() + "]"
);
        }
 
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
        wac.setParent(parent);
        wac.setServletContext(servletContext);
        wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
        customizeContext(servletContext, wac);
        wac.refresh();
 
        return
 wac;
    }

这里很容易理解,只不过它的创建不是直接new,而是封装了一层,调用BeanUtils.instantiateClass()工具方法

接下来设定WebApplicationCcontext实例的parent, servletContext,

其中配置文件位置(web.xml中)的contextConfigLocation参数指定的

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value
>classpath:/SpringContext/applicationContext-web.xml
    </param-value
>
</context-param>

倒数第二行调用的customizeContext()方法目前Spring实现代码是空的,估计留作以后(看来万事要留余地啊^_^)

最后一件事就是调用WebApplicationContext的refresh() 方法。(这个方法是个stratup方法,很重要。他干的事情后面会着重涉及)

 

最后就是真正做事的initWebApplicationContext()方法:

public
 WebApplicationContext initWebApplicationContext(ServletContext servletContext)
            throws IllegalStateException, BeansException {
 
        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.
            this
.context = createWebApplicationContext(servletContext, parent);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this
.context);
            currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), 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;
        }
    }

它其实做得事情很简单,调用loadParentContext()方法获取父context,调用createWebApplicationContext()创建WebApplicationContext实例

 

一切都似乎完成了,至少我们明白了配置文件是如何加载进去的,至于IoC容器如何帮你注入配置文件中的bean,下次再探索。线索是什么呢?你还记 得在createWebApplication()的最后一步做什么了吗?refresh(), 对了,这个就是你作为职业"嘿客"要探索的下一个线索

分享到:
评论

相关推荐

    spring-context 源代码

    spring-context 源代码spring-context 源代码spring-context 源代码spring-context 源代码

    Spring动态加载配置文件

    在Spring框架中,动态加载配置文件是一项重要的功能,它使得开发者在开发过程中无需重启应用就能实时更新配置,极大地提高了开发效率。热部署方案是这一功能的具体应用,它允许我们在不中断服务的情况下,对应用程序...

    Spring源代码解析(一):IOC容器.doc

    在Spring源代码解析的第一部分,我们将聚焦于IOC容器,特别是BeanFactory接口,它是所有Spring容器的基础。 BeanFactory接口是Spring的基石,它定义了基本的容器操作,如获取Bean、检查Bean是否存在、确定Bean的...

    JavaEE源代码 spring-context

    JavaEE源代码 spring-contextJavaEE源代码 spring-contextJavaEE源代码 spring-contextJavaEE源代码 spring-contextJavaEE源代码 spring-contextJavaEE源代码 spring-contextJavaEE源代码 spring-contextJavaEE源...

    Spring中使用classpath加载配置文件浅析

    - 当配置文件被打包在conf.jar文件内时,上述代码将加载jar文件内conf目录下的`application-context.xml`文件。 - 如果同时存在目录和jar包,且没有使用classpath*前缀,Spring默认只会加载目录下的配置文件。 2...

    浅谈SpringBoot2.4 配置文件加载机制大变化

    Spring Boot 2.4 中的配置文件加载机制发生了重大变化,这些变化将影响到应用程序的配置文件加载方式。下面是相关知识点的详细介绍: 一、为什么要进行这些更改 随着最新版本 Spring Boot 的发布,Spring 一直在...

    Spring中如何加载多个配置文件.pdf

    在Spring框架中,加载多个配置文件是常见的需求之一。这不仅可以帮助我们更好地组织代码结构,还可以提高程序的可维护性和可扩展性。本文将详细介绍Spring框架中如何加载多个配置文件的不同方式,并提供具体的示例...

    Spring 加载多个配置文件

    ### Spring 加载多个配置文件详解 #### 一、引言 在现代软件开发中,Spring 框架因其强大的依赖注入(DI)和面向切面编程(AOP)能力而备受青睐。尤其在构建大型应用时,为了提高代码的可读性和可维护性,将系统...

    Spring配置文件spring-context.zip

    "Spring配置文件spring-context.zip"包含了Spring框架中的上下文配置,这是Spring管理对象及服务的核心。 `applicationContext.xml`是Spring应用上下文的主配置文件,它定义了bean的声明、bean之间的依赖关系以及...

    Spring 源代码解析

    Spring源代码解析1:IOC容器;Spring源代码解析2:IoC容器在Web容器中的启动;Spring源代码解析3:Spring JDBC ; Spring源代码解析4:Spring MVC ;Spring源代码解析5:Spring AOP获取Proxy;Spring源代码解析6:...

    加载jar包中的spring配置文件

    例如,`classpath:spring-context.xml`意味着Spring会尝试在类路径下的`spring-context.xml`文件中加载配置。 3. **`classpath*:`前缀**:这个前缀与`classpath:`类似,但有一个重要的区别。`classpath*:`不仅会...

    spring 1.2源代码

    通过阅读这些源代码,开发者可以学习到Spring如何实现依赖注入(Dependency Injection,DI),这是Spring的核心特性之一。DI使得对象之间的耦合度降低,提高了代码的可测试性和可维护性。此外,你还能看到AOP(面向...

    如何加载jar包中的spring配置文件

    在Spring MVC项目中,加载jar包中的Spring配置文件是一个常见的需求,特别是在进行SSM(Spring、Spring MVC、MyBatis)整合时。SSM框架的整合通常涉及到多个配置文件的组织和管理,其中一部分配置可能会被打包到独立...

    利用Spring Context上下文创建自定义对象

    在Spring应用中,Context通常通过`ApplicationContext`接口来实例化,如`ClassPathXmlApplicationContext`或`FileSystemXmlApplicationContext`,这些类用于加载XML配置文件并初始化bean。 创建自定义对象的基本...

    Spring3.0 配置文件中加载Properties文件的小例子

    接下来,我们将在Spring的配置文件(如`applicationContext.xml`)中声明一个`PropertyPlaceholderConfigurer` bean,它负责加载并解析Properties文件。配置如下: ```xml class="org.springframework.beans....

    Spring Boot多模块配置文件读取

    在Spring Boot应用中,多模块配置文件的读取是一个重要的实践,它有助于提高代码的可维护性和模块化。本文将详细探讨如何在Spring Boot的多模块项目中管理和使用不同的配置文件,以实现低耦合的设计。 首先,了解...

    spring完整源代码

    绝对完整,例如: spring-aop spring-adpects ...spring-context spring-context-support spring-core spring-expression spring-framework-bom spring-instrument spring-instrument-tomcat spring-jdbc 等等

    spring读取配置文件

    这两个类都是Spring用于创建应用上下文的实现,主要用来加载XML配置文件并初始化Spring容器。 1. `ClassPathXmlApplicationContext`:此上下文主要用于加载类路径(classpath)下的XML配置文件。这意味着配置文件应...

    pro spring3.0源代码

    8. **Spring Boot**:尽管书名是《Pro Spring 3.0》,但源代码可能也涵盖了Spring Boot,这是一个基于Spring的快速开发框架,简化了Spring应用的初始设置和配置。 通过对这些源代码的深入学习,开发者可以更全面地...

Global site tag (gtag.js) - Google Analytics