体能状态先于精神状态,习惯先于决心,聚焦先于喜好
上一篇文章提到类这一点 2、Spring源码学习:认识加载 xml 文件的 ClassPathXmlApplicationContext
从其定义(org.springframework.context.ConfigurableApplicationContext.refresh() )来看,用于加载或者刷新来自 XML 文件、properties 文件 或者关系数据库的配置
是一个在开始阶段调用的方法,简单说,其负责加载的(non-lazy-init)单例 bean 要么全部成功,要么全部失败
refresh()的实现方法位于 org.springframework.context.support.AbstractApplicationContext
//进去 debug 跟踪 refresh()方法即可
ApplicationContext application=new ClassPathXmlApplicationContext("classpath:applicationContext2.xml");
refresh() 内部调用方法 org.springframework.context.support.AbstractApplicationContext.prepareRefresh(
准备刷新上下文,设置开始时间和 active flag 未true
这个 active flag 是作为 是否需要刷新 applicatonContext 对象的标志
因为 在 Spring 容器加载的初期, refresh()方法都会被调用,本文开启了另一个入口
org.springframework.context.support.GenericApplicationContext
这个类可以为我们提供更灵活的配置,即手动去设置配置文件,以及手动调用 refresh() 方法
@Test
public void testRefresh() {
//org.springframework.context.support.GenericApplicationContext
GenericApplicationContext ctx = new GenericApplicationContext();
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
//文件位于 src/main/resources 目录下
xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
//从这里进去源码
ctx.refresh();
//MyBean myBean = (MyBean) ctx.getBean("myBean");
}
用于表示 refresh() 方法与其他接口或类关系的UML 关系图
简单来说,Spring 创建一个webApplicationContext 之后,需要进行 refresh()动作,其中最主要的就是加载指定的 Spring 的配置文件。
并且将在这个方法中修改if (!cwac.isActive()) {
判断是是否激活标志 传送门
加载或者刷新持久化对配置信息,该配置可能是一个XML 文件,properties 文件,或者关系型数据库
由于这是一个启动阶段对方法,如果启动失败对化,所有已经被创建对单例bean都需要被销毁,以避免不稳定对资源出现.换句话说,这个方法被调用完毕后,要么所有的单例bean被加载到(上下文),要么一个都没有被加载.
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//刷新之前的准备工作,包括设置启动时间,是否激活标识位,初始化属性源(property source)配置
//这里将beanFactory 是否激活修改为 true
prepareRefresh();
// 通知子类刷新自己的 BeanFactory (将 bean定义转化为 beandefination 对象,添加到 BeanFactory,但是未实例化)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 准备 beanFactory 供上下文使用(context,看具体实现的子类,设置ClassLoader,Spel解析器,忽略部分接口的注入,实例化部分类,添加后置处理器-postprocess)
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//模版方法:允许子类对 beanFactory 进行 后续处理(post-processing),webXmlApplicationContext 设置了scope等参数
postProcessBeanFactory(beanFactory);
// 实例化、注册并调用 beanFactory 中作为工厂处理器的 后置处理器bean,如 PropertyPlaceholderConfigurer(处理占位符)
invokeBeanFactoryPostProcessors(beanFactory);
// 实例化和注册beanFactory中扩展了BeanPostProcessor的后置处理器bean,如 AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化 消息国际化工具类 MessageSource
initMessageSource();
// Initialize event multicaster for this context.
// 初始化 事件 组播处理器
initApplicationEventMulticaster();
// 模版方法:初始化子类上下文中被特殊定义的bean;Spring 提供的是一个空方法,供子类扩展
onRefresh();
// 检查并注册事件监听器bean,广播early application events
registerListeners();
// 实例化所有non-lazy-init(非懒加载的) 的单例bean,实例化的过程各种 BeanPostProcessor 开始起作用
finishBeanFactoryInitialization(beanFactory);
// 最后一步:发布相应的事件-上下文刷新工作完成了
//清除上下文资源缓存(如扫描中的ASM元数据);初始化上下文的生命周期处理器,并刷新(找出Spring容器中实现了Lifecycle接口的bean并执行start()方法)。发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - "+"cancelling refresh attempt: " + ex);
}
//catch逻辑:销毁已经被创建的单例 bean,避免不稳定资源的出现
destroyBeans();
//catch逻辑:重置 ‘active’标志
cancelRefresh(ex);
// 继续抛出异常给调用者
throw ex;
}
finally {
// finally逻辑:重置 Spring's core 的缓存信息,因为我们可能不会再用到这些单例bean的元数据信息了
resetCommonCaches();
}
}
}
org.springframework.context.support.AbstractApplicationContext.prepareRefresh(
准备刷新上下文,设置开始时间和 active flag 以便于开始任何配置文件的初始化动作。
这里的 active flag 默认为 false,一般作为 新创建 webApplicationContext 对象是否刷新的标志 如 传送门
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// 初始化任何通配符匹配的文件,你可以任务该方法之后,操作系统相关、JVM相关信息都被加载了,准备的参数可以通过 getEnvironment() 来获取
initPropertySources();
//检测必须参数的非空性,通过 ConfigurablePropertyResolver#setRequiredProperties 设置
//debug发现,默认情况该方法没啥用-没有需要检测的从参数
getEnvironment().validateRequiredProperties();
//实例化LinkedHashSet,允许早期的 ApplicationEvents 集合在 multicaster 可用时被发布
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
本方法用于将配置文件中的bean加载到 beanDefination 中,然后将beanDefination 放入到 beanFactory,但是需要注意到是,只是加载了 bean到定义信息,还没有进行注入、实例化等工作.
限于篇幅,单独起一篇文章
3.2ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
本方法用于设置 beanFactory 的类加器、SpEL解析器、bean的属性编辑器、特殊接口的免自动注入和特殊类型的指定注入,环境对象设置为单例
限于篇幅,单独写一篇文章
3.3 prepareBeanFactory(beanFactory);
代码运行到这里,Spring 上下文已经将基本的 beanFactory 和 beanDefination 准备好了。
该方法允许子类对 beanFactory 进行进一步的后置处理;
该方法默认为空方法,允许子类根据需要选择是否覆盖重写,体现了模板方法模式;
具体来说,如果子类为 ClassPathApplicationContext ,那么这个方法依旧是一个空方法
Spring 中的事件允许不同的bean之间的信息交换,体现了观察者模式。
事件可以是既定的事件,也可以是自定义的事件,事件发布后由多播处理器统一处理,对所有的监听器进行事件转发——该过程可以是多线程异步的。更详细的介绍可以看下文
Spring中的事件发布和处理
该方法与 beanFactory 相关的后置处理器的初始化、注册和调用
重点关注 ConfigurationClassPostProcessor 这个后置处理器,用于处理代码中的
@Component、@Configuration、@ComponentScan 、@Bean 注解的处理
可以理解为,Spring Bean 可以来自于 XML定义,也可以来自 Java 代码中的定义.
该方法仅仅用于与 普通 Spring bean 相关的后置处理器的处理
配置 i18n 即国际化资源
i18n项目源码
初始化应用的事件多播处理器,事件发布后由该处理器统一进行处理
默认是空实现,给子类扩展的空间,又是模版方法模式呦
注册监听器,比如上下文完成刷新事件
也可以自定义监听器,由于多播处理器并不会区分监听器,所以需要监听器自己区分不同的事件进行针对性处理。
[1]、https://www.jianshu.com/p/bede81fad45c