spring源码学习笔记(一)扩展点之SmartInitializingSingleton源码解析(从xxl-job集成spring说起)
每天多学一点点~
话不多说,这就开始吧…
文章目录
1.前言
之前用到了xxl-job分布式调度框架,很好奇它是如何集成spring的。正好过年在家闲的无事翻看了一下,发现其XxlJobSpringExecutor实现了SmartInitializingSingleton接口。
SmartInitializingSingleton作为spring的扩展点之一,在bean实例化完成,包括依赖注入完成,BeadPostProcess,InitializingBean,initMethod等等全部完成后,执行;可以理解为bean的收尾操作;
2. 从xxl-job集成spring说起
在xxl-job中,XxlJobConfig类中,注册了 @Bean XxlJobSpringExecutor这个类
XxlJobSpringExecutor
- 首先实现了ApplicationContextAware接口(这个接口其实用的还是比较多的,比如各种SpringBeanUtils工具类,目的是拿到applicationContext)
- 然后XxlJobSpringExecutor又实现了SmartInitializingSingleton接口,则当所有单例 bean 都初始化完成以后,Spring的IOC容器会回调该接口的 afterSingletonsInstantiated()方法。
其中,MethodIntrospector.selectMethods是属于spring4.x开始比较新的方法,是spring-core中一个频繁使用的工具类。用于搜索与元数据相关的方法的算法,包括接口和父类,同时还处理参数化方法以及基于接口和基于类的代理遇到的常见情况。通常(但不是必须)用于查找带注释的处理程序方法。
实现SmartInitializingSingleton后,当所有单例 bean 都初始化完成以后, 容器会回调该接口的方法 afterSingletonsInstantiated。
主要应用场合就是在所有单例 bean 创建完成之后,可以在该回调中做一些事情。
3. SmartInitializingSingleton源码解析
知道了SmartInitializingSingleton的用法,再深入看看其实如何实现的。
首先,看过spring源码的,应该对ioc容器的加载过程都不陌生,我们直接进入refresh()方法。
org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(java.lang.Class<?>...)
--->org.springframework.context.support.AbstractApplicationContext#refresh
---> org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
--->org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
beanFactory.preInstantiateSingletons(); 实例化剩余的单例bean。(不包含懒加载的)
再来看看 preInstantiateSingletons 方法,主要逻辑就在其中
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Pre-instantiating singletons in " + this);
}
//获取我们容器中所有bean定义的名称
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
//循环我们所有的bean定义名称
for (String beanName : beanNames) {
//合并我们的bean定义
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
/**
* 根据bean定义判断是不是抽象的&& 不是单例的 &&不是懒加载的
*/
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//是不是工厂bean
if (isFactoryBean(beanName)) {
//是的话 给beanName+前缀&符号
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
} else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
//调用真正的getBean的流程
if (isEagerInit) {
getBean(beanName);
}
}
} else {
//非工厂Bean 就是普通的bean!!
getBean(beanName);
}
}
}
//或有的bean的名称 ...........到这里所有的单实例的bean已经记载到单实例bean到缓存中
for (String beanName : beanNames) {
//从单例缓存池中获取所有的对象
Object singletonInstance = getSingleton(beanName);
//判断当前的bean是否实现了SmartInitializingSingleton接口
// !!!!!!!!!!!!!!!!!!!xxl-job 就是通过 SmartInitializingSingleton 接入spring 的 !!!!!!!!!
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
// 调用 实现了 singletonInstance 接口的 afterSingletonsInstantiated 方法
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
} else {
//触发实例化之后的方法afterSingletonsInstantiated
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
从源码分析中得知,当Spring将所有单例 bean 都实例初始化完成以后,如果存在实现SmartInitializingSingleton接口的bean,那么Spring还会调用到该bean的afterSingletonsInstantiated()方法。
4.结语
世上无难事,只怕有心人,每天积累一点点,fighting!!!
2021,加油,fighting,希望可以少些crud啦!