文章目录
- 前言
- 所有初始化 Spring 容器的操作都会调用这个 refresh() 方法
- refresh() 方法 修改 active flag 为true
- 提供一个新入口
- 进入源码
- 对 refresh()中关键代码的解读
- prepareRefresh();
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- prepareBeanFactory(beanFactory);
- postProcessBeanFactory(beanFactory);
- 后置处理器的概念
- initMessageSource();
- initApplicationEventMulticaster();
- onRefresh();
- registerListeners();
- finishBeanFactoryInitialization(beanFactory);
- finishRefresh();
- 初始化期间出现异常
- 参考资料
前言
体能状态先于精神状态,习惯先于决心,聚焦先于喜好
所有初始化 Spring 容器的操作都会调用这个 refresh() 方法
上一篇文章提到类这一点 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() 方法 修改 active flag 为true
refresh() 内部调用方法 org.springframework.context.support.AbstractApplicationContext.prepareRefresh(
准备刷新上下文,设置开始时间和 active flag 未true
这个 active flag 是作为 是否需要刷新 applicatonContext 对象的标志
提供一个新入口
GenericApplicationContext
因为 在 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 关系图
用于表示 refresh() 方法与其他接口或类关系的UML 关系图
进入源码
refresh()
简单来说,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 中 扩展了 BeanFactoryPostProcessor 接口的beanFactory 处理器,比如ConfigurationClassPostProcessor 用于 @Configuration、@Bean、@PropertySource 标签的处理,在普通普通 bean 实例化前调用
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();
}
}
}
对 refresh()中关键代码的解读
prepareRefresh();
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>();
}
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
本方法用于将配置文件中的bean加载到 beanDefination 中,然后将beanDefination 放入到 beanFactory,但是需要注意到是,只是加载了 bean到定义信息,还没有进行注入、实例化等工作.
限于篇幅,单独起一篇文章
3.2ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
本方法用于设置 beanFactory 的类加器、SpEL解析器、bean的属性编辑器、特殊接口的免自动注入和特殊类型的指定注入,环境对象设置为单例
限于篇幅,单独写一篇文章
3.3 prepareBeanFactory(beanFactory);
postProcessBeanFactory(beanFactory);
代码运行到这里,Spring 上下文已经将基本的 beanFactory 和 beanDefination 准备好了。
该方法允许子类对 beanFactory 进行进一步的后置处理;
该方法默认为空方法,允许子类根据需要选择是否覆盖重写,体现了模板方法模式;
具体来说,如果子类为 ClassPathApplicationContext ,那么这个方法依旧是一个空方法
后置处理器的概念
Spring 中的事件允许不同的bean之间的信息交换,体现了观察者模式。
事件可以是既定的事件,也可以是自定义的事件,事件发布后由多播处理器统一处理,对所有的监听器进行事件转发——该过程可以是多线程异步的。更详细的介绍可以看下文
Spring中的事件发布和处理
invokeBeanFactoryPostProcessors(beanFactory);:修饰 beanDefination
该方法与 beanFactory 相关的后置处理器的初始化、注册和调用
该方法主要用于处理 beanDefination,此外进一步对 configuration 修饰的类中标注的 beandefination 进行发掘
for (BeanDefinitionRegistryPostProcessor postProcessor : registryPostProcessorBeans) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
在 postProcessBeanDefinitionRegistry 方法内部是对配置类对具体处理——发掘使用注解注册的beanDefinition
processConfigBeanDefinitions(registry);
重点关注 ConfigurationClassPostProcessor 这个后置处理器,用于处理代码中的
@Component、@Configuration、@ComponentScan 、@Bean 注解的处理
可以理解为,Spring Bean 可以来自于 XML定义,也可以来自 Java 代码中的定义.
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
可以自己实现 BeanFactoryPostProcessor 接口,该接口注解中有描述:允许修改 beanFactory中的 beanDefination 但是千万不要进行任何可能导致bean实例化的操作,一来破坏了Spring的组织结构,而来会导致注解注入无法生效——对于注解逻辑到处理还没到呢——除非你是用xml注入——所以千万不要在自定义到 BeanFactoryPostProcessor 到逻辑中进行任何实例化的操作.
自定义处理器和默认处理器可以有一个优先级设置——实现 PriorityOrdered 接口,在调用处理器时会按照排序进行
registerBeanPostProcessors(beanFactory);:修饰bean
该方法会实例化、注册、调用 beanFactory中扩展了 BeanPostProcessor 的后置处理器bean,如 AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
Must be called before any instantiation of application beans(必须在应用上下文实例化前调用这些处理器)
该方法仅仅用于与 普通 Spring bean 相关的后置处理器的处理
允许自己实现 BeanPostProcessor ,参与 bean 实例化过程的处理,允许实现 PriorityOrdered 可口以实现优先级处理
延伸阅读:Spring 中关于 多个BeanPostProcessor代理同个Bean的问题
initMessageSource();
配置 i18n 即国际化资源
i18n项目源码
initApplicationEventMulticaster();
初始化应用的事件多播处理器,事件发布后由该处理器统一进行处理
onRefresh();
默认是空实现,给子类扩展的空间,又是模版方法模式呦
registerListeners();
注册监听器,比如上下文完成刷新事件
也可以自定义监听器,由于多播处理器并不会区分监听器,所以需要监听器自己区分不同的事件进行针对性处理。
可以自定义监听器
Spring中的事件发布和处理
finishBeanFactoryInitialization(beanFactory);
实例化所有未实例化且非懒加载的单例对象
/**
* ConfigurableListableBeanFactory beanFactory
* Instantiate all remaining (non-lazy-init) singletons.
/*
beanFactory.preInstantiateSingletons();
创建 bean时会先判断该bean是否已经创建或者正在创建
- 已经创建 DefaultSingletonBeanRegistry.java
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
- 正在创建 DefaultSingletonBeanRegistry.java
/** Names of beans that are currently in creation (using a ConcurrentHashMap as a Set) */
private final Map<String, Boolean> singletonsCurrentlyInCreation = new ConcurrentHashMap<String, Boolean>(16);
如果开始初始化一个 bean,会将这个name放入下面的map中,value=true
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.containsKey(beanName) &&
this.singletonsCurrentlyInCreation.put(beanName, Boolean.TRUE) != null) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
创建完毕后要移除
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.containsKey(beanName) &&
!this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
- 创建一个bean之后的处理
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
- 判断循环依赖的逻辑
已在创建的bean和当前创建的名字一样 AbstractBeanFactory.java
/** Names of beans that are currently in creation */
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<Object>("Prototype beans currently in creation");
- 已经至少被创建了一次 AbstractBeanFactory.java
/**
* Names of beans that have already been created at least once
* (using a ConcurrentHashMap as a Set)
*/
private final Map<String, Boolean> alreadyCreated = new ConcurrentHashMap<String, Boolean>(64);
finishRefresh();
protected void finishRefresh() {
// 初始化上下文的生命周期
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// 发布事件 ContextRefreshedEvent ,监听器可以收到这个事件
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
初始化期间出现异常
destroyBeans();
catch逻辑:销毁所有已经创建的单利 bean
cancelRefresh(ex);
catch逻辑:重置 ‘active’标志
参考资料
[1]、https://www.jianshu.com/p/bede81fad45c