基于Xml 的 IOC 容器的初始化(二)加载 (上)

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

以下是基于基于 Xml 的 IOC 容器的初始化的加载,容器的定位请看上篇 基于Xml 的 IOC 容器的初始化(一)定位

3、开始启动

SpringIOC 容器对 Bean 配置资源的载入是从 refresh()函数开始的,refresh()是一个模板方法,规定了 IOC 容 器 的 启 动 流 程 , 有 些 逻 辑 要 交 给 其 子 类 去 实 现 。 它 对 Bean 配 置 资 源 进 行 载 入 ClassPathXmlApplicationContext 通过调用其父类 AbstractApplicationContext 的 refresh() 函数启 动整个 IOC 容器对 Bean 定义的载入过程,现在我们来详细看看 refresh() 中的逻辑处理:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
    @Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 1、调用容器准备刷新的方法,获取容器当前的时间,同时设置同步标识
			prepareRefresh();

			// 告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
			//子类的refreshBeanFactory()方法启动
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			//3、为BeanFactory配置容器特性,例如类加载器、事件处理器等
			prepareBeanFactory(beanFactory);

			try {
				//4、为工期的某些子类指定特殊的BeanPost事件处理器
				postProcessBeanFactory(beanFactory);

				//5、调用所有注册的BeanFactoryPostProcessor的Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				//6、为BeanFactory注册BeanPost时间处理器。
                //BeanPostProcessor是Bean后置处理器,用于监听容器触发事件
				registerBeanPostProcessors(beanFactory);

				//7、初始化信息源,和国际化有关
				initMessageSource();

				//8、初始化容器事件传播器
				initApplicationEventMulticaster();

				//9、调用子类的某些特殊Bean初始化方法
				onRefresh();

				//10、为事件传播器注册事件监听器
				registerListeners();

				//11、初始化所有剩余的单例Bean
				finishBeanFactoryInitialization(beanFactory);

				//12、初始化容器的生命周期事件处理器,并发布容器的生命周期事件
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				//13、销毁已创建的Bean
				destroyBeans();

				//14、取消refresh操作,重置容器的同步标识
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
                //重设公共缓存
				resetCommonCaches();
			}
		}
	}
}

refresh()方法主要为 IOC 容器 Bean 的生命周期管理提供条件,Spring IOC 容器载入 Bean 配置信息 从 其 子 类 容 器 的 refreshBeanFactory() 方 法 启 动 , 所 以 整 个 refresh() 中 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这句以后代码的 都是注册容器的信息源和生命周期事件,我们前面说的载入就是从这句代码开始启动。

refresh() 方法的主要作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和 关闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。它类似于对 IOC 容器的重启,在新建立 好的容器中对容器进行初始化,对 Bean 配置资源进行载入。

4、创建容器

obtainFreshBeanFactory()方法调用子类容器的 refreshBeanFactory()方法,启动容器载入 Bean 配置 信息的过程,代码如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
    //这里只是定义了方法,并没有实现
    protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
    
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}
}

AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory()方法,容器真正调用的是 其子类 AbstractRefreshableApplicationContext 实现的 refreshBeanFactory()方法,方法的源 码如下:

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		//如果已经有容器,销毁容器中的bean,关闭容器
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//创建IOC容器
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等
			customizeBeanFactory(beanFactory);
			//调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
}

在这个方法中,先判断 BeanFactory 是否存在,如果存在则先销毁 beans 并关闭 beanFactory,接着 创建 DefaultListableBeanFactory,并调用 loadBeanDefinitions(beanFactory) 装载 bean 定义。

5、载入配置路径

AbstractRefreshableApplicationContext 类的中只是定义了抽象的 loadBeanDefinitions(beanFactory) 方法,实际是调用了 AbstractXmlApplicationContext 的

loadBeanDefinitions(beanFactory) 方法,,该方法的源码如下:

//注意看这里继承了AbstractRefreshableApplicationContext类
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {

	//实现父类抽象的载入Bean定义方法
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		//创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容  器使用该读取器读取Bean定义资源
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		//为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
		//祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		//为Bean读取器设置SAX xml解析器
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		//当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
		initBeanDefinitionReader(beanDefinitionReader);
		//Bean读取器真正实现加载的方法
		loadBeanDefinitions(beanDefinitionReader);
	}
    
    //Xml Bean读取器加载Bean定义资源
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		//获取Bean定义资源的定位
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
			//的Bean定义资源
			reader.loadBeanDefinitions(configResources);
		}
		//如果子类中获取的Bean定义资源定位为空,
		//则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
        //是执行了这里的Bean资源的定义!
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
			reader.loadBeanDefinitions(configLocations);
		}
	}
    
    //这里又使用了一个委托模式,调用子类的获取Bean定义资源定位的方法
	//该方法在ClassPathXmlApplicationContext中进行实现,对于我们
	//举例分析源码的FileSystemXmlApplicationContext没有使用该方法(返回null)
	@Nullable
	protected Resource[] getConfigResources() {
		return null;
	}
    
}

以 XmlBean 读取器的其中一种策略 XmlBeanDefinitionReader 为例。XmlBeanDefinitionReader 调 用其父类AbstractBeanDefinitionReader 的 reader.loadBeanDefinitions() 方法读取Bean配置资源。

由于我们使用 ClassPathXmlApplicationContext 作为例子分析,因此 getConfigResources 的返回值 为 null,因此程序执行 reader.loadBeanDefinitions(configLocations) 分支。

6、分配路径处理策略

在 XmlBeanDefinitionReader 的抽象父类 AbstractBeanDefinitionReader 中定义了载入过程。

AbstractBeanDefinitionReader 的 loadBeanDefinitions() 方法源码如下:

public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
    
    //重载方法,调用下面的loadBeanDefinitions(String, Set<Resource>);方法
    //执行顺序2
	@Override
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}
    
    //执行顺序3
    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) 
        throws BeanDefinitionStoreException {
		//获取在IoC容器初始化过程中设置的资源加载器
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				//将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
				//加载多个指定位置的Bean定义资源文件
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			//将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
			//加载单个指定位置的Bean定义资源文件
			Resource resource = resourceLoader.getResource(location);
			//委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}
    
    //重载方法,调用loadBeanDefinitions(String);
    //第5点中 AbstractXmlApplicationContextd 类中的 reader.loadBeanDefinitions(configLocations);执行到这里
    //执行顺序1
	@Override
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int counter = 0;
		for (String location : locations) {
			counter += loadBeanDefinitions(location);
		}
		return counter;
	}

}

第四步的创建容器 AbstractRefreshableApplicationContext 的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 方法实际上就是调用了 AbstractBeanDefinitionReader 的 loadBeanDefinitions() 方法。

从对 AbstractBeanDefinitionReader 的 loadBeanDefinitions() 方法源码分析可以该方法就做了两件事:

  • 首先,调用资源加载器的获取资源方法 resourceLoader.getResource(location),获取到要加载的资源。
  • 其次,真正执行加载功能是其子类 XmlBeanDefinitionReader 的 loadBeanDefinitions() 方法。。

在 AbstractBeanDefinitionReader 中的 loadBeanDefinitions()方法中调用了 AbstractApplicationContext 的 getResources() 方法(是因为AbstractApplicationContext 实现了 ResourceLoader ),跟进去之 后发现 getResources() 方法其实定义在 ResourcePatternResolver 中,此时,我们有必要来看一下 ResourcePatternResolver 的全类图:
在这里插入图片描述

从上面可以看到 ResourceLoader 与 ApplicationContext 的继承关系,可以看出调用 AbstractApplicationContext 的 getResources() 方法其实际调用的是 DefaultResourceLoader 中 的 getSource() 方 法 定 位 Resource , 因 为 ClassPathXmlApplicationContext 本身就是 DefaultResourceLoader 的实现类,所以此时又回到了 ClassPathXmlApplicationContext 中来。

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

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

(0)
小半的头像小半

相关推荐

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