基于 Xml 的 IOC 容器的初始化(一)定位

导读:本篇文章讲解 基于 Xml 的 IOC 容器的初始化(一)定位,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

IOC 容器的初始化包括 BeanDefinition 的Resource定位加载注册这三个基本的过程。我们以 ApplicationContext 为例讲解, ApplicationContext 系列容器也许是我们最熟悉的,因为 Web 项目中使用的 XmlWebApplicationContext 就属于这个继承体系,还有 ClassPathXmlApplicationContext 等,其继承体系如下图所示:
在这里插入图片描述
ApplicationContext 允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于 Bean 的查找 可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的 Spring 应用提供了一个共享的 Bean 定义环境。

1、寻找路口

还有一个我们用的比较多的 ClassPathXmlApplicationContext,通过 main() 方法启动:

ApplicationContext app = new ClassPathXmlApplicationContext("application.xml"); 

先看其构造函数的调用:

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
    this(new String[] {configLocation}, true, null);
}

实际调用为:

public ClassPathXmlApplicationContext(
    String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
    throws BeansException {

    super(parent);
    //调用父类的方法去设置Bean的配置信息的定位路径
    setConfigLocations(configLocations);
    if (refresh) {
        refresh();
    }
}

还有想 AnnotationConfigApplicationContext、FileSystemXmlApplicationContext、XmlWebApplicationContextt 等都继承自父容器 AbstractApplicationContext 主要用到了装饰器模式 和策略模式,最终都是调用 refresh() 方法。

2、获取配置路径

通 过 分 析 ClassPathXmlApplicationContext 的 源 代 码 可 以 知 道 , 在 创 建 ClassPathXmlApplicationContext 容器时,构造方法做以下两项重要工作:

  • 首先,调用父类容器的构造方法(super(parent)方法)为容器设置好 Bean 资源加载器。
  • 然 后 , 再 调 用 父 类 AbstractRefreshableConfigApplicationContext 的 setConfigLocations(configLocations)方法设置 Bean 配置信息的定位路径。

通 过 追 踪 ClassPathXmlApplicationContext 的 继 承 体 系 , 发 现 其 父 类 的 父 类 AbstractApplicationContext 中初始化 IOC 容器所做的主要源码如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
    //静态代码块,在整个容器创建过程中只执行一次
    static {
        //为了避免应用程序在 Weblogic8.1 关闭时出现类加载异常加载问题,加载 IOC 容
        //器关闭事件(ContextClosedEvent)类
        ContextClosedEvent.class.getName();
    }
    
    public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		this();
		setParent(parent);
	}
    
    public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}
    
    获取一个 Spring Source 的加载器用于读入 Spring Bean 配置信息
    protected ResourcePatternResolver getResourcePatternResolver() {
        //AbstractApplicationContext 继承 DefaultResourceLoader,因此也是一个资源加载器
        //Spring 资源加载器,其 getResource(String location)方法用于载入资源
        return new PathMatchingResourcePatternResolver(this);
    }
}

AbstractApplicationContext 的默认构造方法中有调用 PathMatchingResourcePatternResolver 的 构造方法创建 Spring 资源加载器:

private final ResourceLoader resourceLoader;

public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
   Assert.notNull(resourceLoader, "ResourceLoader must not be null");
   //设置Spring的资源加载器
   this.resourceLoader = resourceLoader;
}

在设置容器的资源加载器之后,接下来 ClassPathXmlApplicationContext 执行 setConfigLocations() 方法通过调用其父类AbstractRefreshableConfigApplicationContext 的方法进行对 Bean 配置信息的 定位,该方法的源码如下:

public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
		implements BeanNameAware, InitializingBean {
	/**
	 * Set the config locations for this application context in init-param style,
	 * i.e. with distinct locations separated by commas, semicolons or whitespace.
	 * <p>If not set, the implementation may use a default as appropriate.
	 */
	public void setConfigLocation(String location) {
		//String CONFIG_LOCATION_DELIMITERS = ",; /t/n";
		//即多个资源文件路径之间用” ,; \t\n”分隔,解析成数组形式
		setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
	}

	/**
	 * Set the config locations for this application context.
	 * <p>If not set, the implementation may use a default as appropriate.
	 */
	//解析 Bean 定义资源文件的路径,处理多个资源文件字符串数组
	public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				// resolvePath 为同一个类中将字符串解析为路径的方法
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}
}

通过这两个方法的源码我们可以看出,我们既可以使用一个字符串来配置多个 Spring Bean 配置信息, 也可以使用字符串数组,即下面两种方式都是可以的:

ClassPathResource res = new ClassPathResource("a.xml,b.xml"); 

多个资源文件路径之间可以是用, ; \t\n等分隔。

ClassPathResource res =new ClassPathResource(new String[]{"a.xml","b.xml"}); 

至此,SpringIOC 容器在初始化时将配置的 Bean 配置信息定位为 Spring 封装的 Resource。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68409.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!