ApplicationContext

之前的分析都是在XmlBeanFactory的基础上进行的分析,现在我们开始分析ApplicationContext,相比之下ApplicationContext提供了更多的扩展功能.
首先看看使用两个不同的类去加载配置文件在写法上的不同:

  • 使用BeanFactory
BeanFactory bf = new XmlBeanFactory(new ClassPathResource(“beanFactoryTest.xml));
  • 使用ApplicationContext
ApplicationContext bf = new ClassPathXmlApplicationContext("beanFactoryTest.xml");

同样以ClassPathXmlApplicationContext作为切入点,开始对整体功能进行分析

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
	this(new String[]{configLocation}, true, (ApplicationContext)null);
}
	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, 	@Nullable ApplicationContext parent) throws BeansException {
        super(parent);
        // 设置配置路径
        this.setConfigLocations(configLocations);
        if (refresh) {
        	// 功能扩展
            this.refresh();
        }

    }

ClassPathXmlApplicationContext可以将配置文件路径以数组的方式传入,ClassPathXmlApplicationContext可以对数组进行解析并进行加载.对于解析及功能实现都在refresh()中实现.
1、设置配置路径

	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++) {
				// 解析给定路径
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

主要用于解析给定的路径数组,当然,如果数组中包含特殊符号,如${var},那么在resolvePath中会搜寻匹配的系统变量并替换.

2、功能扩展
设置好路径之后,就可以根据路径做配置文件的解析及各种功能的实现类.

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 1、准备刷新上下文环境
			prepareRefresh();

			// 2、初始化beanFactory,并进行XML文件读取
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 3、对beanFactory进行各种功能的填充
			prepareBeanFactory(beanFactory);

			try {
				// 4、子类覆盖方法做额外的处理
				postProcessBeanFactory(beanFactory);

				// 5、激活各种BeanFactory处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// 6、注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用在getBean的时候
				registerBeanPostProcessors(beanFactory);

				// 7、为上下文初始化Message源,即不同语言的消息体,国际化处理
				initMessageSource();

				// 8、初始化应用消息广播器,并放入"applicationEventMulticaster"bean中
				initApplicationEventMulticaster();

				// 9、给子类初始化其他的bean
				onRefresh();

				// 10、在所有注册的bean中查找Listener bean,注册到消息广播器中
				registerListeners();

				// 11、初始化剩下的单实例(非惰性的)
				finishBeanFactoryInitialization(beanFactory);

				// 12、完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出
				// ContextRefreshEvent通知别人
				finishRefresh();
			}

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

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				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();
			}
		}
	}

概括一下上面每一步都做了什么:

  1. 初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证
  2. 初始化BeanFactory,并进行XML文件读取
    ClassPathXmlApplicationContext包含着BeanFactory所提供的一切特征,这里会复用BeanFactory中的配置文件读取解析及其他功能,这一步之后ClassPathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能,也就是可以进行beanDefinition的提取等基础操作了.
  3. 对BeanFactory进行各种功能填充.
  4. 子类覆盖方法做额外的处理.
  5. 激活各种BeanFactoryPostProcessor
  6. 注册拦截bean创建的beanPostProcessor,这里只是注册,真正的调用是在getBean时候.
  7. 为上下文初始化Message源,即对不同语言的消息体进行国际化处理
  8. 初始化应用消息广播器,并放入applicationEventMulticaster的bean中
  9. 留给子类来初始化其他的bean
  10. 在所有注册的bean中查找listener bean,注册到消息广播器中
  11. 初始化剩下的单实例(非惰性的)
  12. 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人.

下面我们先简单分析一下前两步

环境准备

	protected void prepareRefresh() {
		// Switch to active.
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);

		if (logger.isDebugEnabled()) {
			if (logger.isTraceEnabled()) {
				logger.trace("Refreshing " + this);
			}
			else {
				logger.debug("Refreshing " + getDisplayName());
			}
		}

		// 留给子类覆盖
		initPropertySources();

		// 验证需要的属性文件是够都已经放入环境中
		getEnvironment().validateRequiredProperties();

		if (this.earlyApplicationListeners == null) {
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

initPropertySources:符合spring中的开发式结构设计,给用户最大的扩展Spring的能力.用户可以根据自身的需要重写该方法,并在方法中进行个性化的属性处理及设置.
validateRequiredProperties:对属性进行验证
就上面两个方法,举个小例子:
比如有一个需求,工程在运行过程中用到的某个设置(例如Var)是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,工程就不能工作.
我们可以自定义类:

public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

    public MyClassPathXmlApplicationContext(String... configLocations) throws BeansException {
        super(configLocations);
    }

    @Override
    protected void initPropertySources() {
        getEnvironment().setRequiredProperties("Var");
    }
}

上面我们重写了initPropertySources,方法,并且设置了环境变量中需要验证的变量,那么在验证的时候也就是程序到getEnvironment().validateRequiredProperties()的时候,如果系统的环境并有检测到对Var的环境变量,那么将抛出异常.
测试:

ApplicationContext bf = new MyClassPathXmlApplicationContext("beanFactoryTest.xml");
Cat cat = (Cat) bf.getBean("blackCat");

加载BeanFactory
obtainFreshBeanFactory()方法就是用来获取beanFactory的,经过这个方法之后,ApplicationContext就拥有了BeanFactory的全部功能了.

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	// 1、初始化BeanFactory,并进行XML文件的读取解析,并将得到的BeanFactory记录在当前实体的属性中
	refreshBeanFactory();
	// 2、返回当前实体的beanFacotry属性
	return getBeanFactory();
}

refreshBeanFactory

    protected final void refreshBeanFactory() throws BeansException {
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
        	// 创建DefaultListableBeanFactory
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            // 指定序列化Id
            beanFactory.setSerializationId(this.getId());
            // 定制beanFactory
            this.customizeBeanFactory(beanFactory);
            // 记载beanDefinition
            this.loadBeanDefinitions(beanFactory);
            synchronized(this.beanFactoryMonitor) {
            	// 使用全局变量记录BeanFactory类实例
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }
  1. 定制beanFactory
    在基本容器的基础上,添加了是否允许覆盖、是否允许扩展
  	protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
  		// 如果属性allowBeanDefinitionOverriding不为空,设置给beanFactory对象相应属性
  		// 该属性的作用是:是否允许覆盖相同名称的不同定义的对象
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
		// 如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性
		// 此属性的作用:是否允许bean之间存在循环依赖
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

对于允许覆盖和允许依赖的设置这里只是判断了是否为空,如果不为空要进行设置,但是并没有看到在哪里进行了设置.其实可以用子类覆盖的方式:

public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
	.....
	
    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        super.setAllowBeanDefinitionOverriding(true);
        super.setAllowCircularReferences(true);
    }
}
  1. 加载BeanDefinition
    在loadBeanDefinitions方法中首先初始化:XmlBeanDefinitionReader,用它来读取XML文件
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// 为beanFactory创建XmlBeanDefinitionReader
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// 对beanDefinitionReader进行环境变量的设置
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// 对beanDefinitionReader进行设置,可以覆盖
		initBeanDefinitionReader(beanDefinitionReader);
		// 读取加载配置文件
		loadBeanDefinitions(beanDefinitionReader);
	}
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			reader.loadBeanDefinitions(configLocations);
		}
	}

使用XmlBeanDefinitionReader的loadBeanDefinitions方法进行配置文件的加载注册,就是之前讲BeanFactory的时候的套路,因为XmlBeanDefinitionReader已经讲之前初始化的beanFactory注册进去了,所以XmlBeanDefinitionReader所读取的BeanDefinitionHolder都会注册到beanFactory中,也就是这步之后,beanFactory就包含了所有解析好的配置.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值