1. 先看例子
有两个类 A
和 B
相互依赖,且都有被 @Scheduled
注解的方法。
@Slf4j
@Component
public class A {
@Autowired
private B b;
@Scheduled(fixedDelay = 1000)
public void testA() {
log.info("testA execute. b:{}", b);
}
}
@Slf4j
@Component
public class B {
@Autowired
private A a;
@Scheduled(fixedDelay = 1000)
public void testB() {
log.info("testB execute. a:{}", a);
}
}
定义一个切面 ScheduleDisableAspect
用来阻止 @Scheduled
方法的执行。
@Configuration
@Aspect
public class ScheduleDisableAspect {
@Around("@annotation(org.springframework.scheduling.annotation.Scheduled)")
public Object beforePointCut(ProceedingJoinPoint pjp) throws Throwable {
return null;
}
}
启动类 SpringDemoApplication
代码如下所示。
@EnableScheduling
@SpringBootApplication
public class SpringDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDemoApplication.class, args);
}
}
运行启动类,结果如下图所示。
图 1:类 A 中的 @Scheduled 方法 testA 没有被拦截2. 原理分析
2.1 Spring 容器中 bean 的初始化
我们从 SpringApplication.run()
开始分析,关注其 refreshContext()
方法,并定位到 DefaultListableBeanFactory
的 preInstantiateSingletons()
方法。
SpringApplication.run(SpringDemoApplication.class, args);
refreshContext(context);
((AbstractApplicationContext) applicationContext).refresh()
finishBeanFactoryInitialization(beanFactory);
beanFactory.preInstantiateSingletons();
简单看一下 preInstantiateSingletons
方法内部实现。
// 忽略暂时不用关注的代码
public void preInstantiateSingletons() throws BeansException {
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
getBean(beanName);
}
}
}
继续看 getBean
方法,getBean
方法是 AbstractBeanFactory
类中的方法。
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
// 忽略不用关注的代码
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 从 Spring 容器中获取单例对象
// 如果 A 依赖 B,且 B 依赖 A。假设先实例化 A,那么在 A 中注入 B 的时候,B 会获得 A bean提前暴露的引用
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 先初始化当前 bean dependsOn 的其他 beans
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
getBean(dep);
}
}
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
return (T) bean;
}
2.2 DefaultSingletonBeanRegistry
中的 getSingleton()
方法
从图 2 可以知道,DefaultSingletonBeanRegistry
是 AbstractBeanFactory
的祖先类。
getSingleton()
方法有两类。
- 一种是创建 bean 时用到的,有
ObjectFactory
参数。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
try {
// 调用 objectFactory 的 getObject 方法
// 即上文提到的 createBean(beanName, mbd, args);
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 添加 bean 到 singletonObjects 缓存
this.singletonObjects.put(beanName, singletonObject);
// 删除 singletonFactories 缓存
this.singletonFactories.remove(beanName);
// 删除 earlySingletonObjects 缓存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
- 一种获取 bean 时用到的,没有
ObjectFactory
参数。
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 从 singletonObjects 获取bean实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 2. 从 earlySingletonObjects 获取 bean 实例
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 3. 从 singletonFactories 获取 ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 3. 从 ObjectFactory 获取 bean 实例
// 重点:下文中的 AbstractAutowireCapableBeanFactory 的 getEarlyBeanReference 方法就是在这里调用的
singletonObject = singletonFactory.getObject();
// 添加 bean 实例到 earlySingletonObjects 缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 删除对应的 singletonFactories 缓存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
addSingletonFactory
方法,在下文中的 createBean
方法中会用到。
// 添加 ObjectFactory 到 singletonFactories 缓存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
2.3 AbstractAutowireCapableBeanFactory
中的 createBean
方法
从图 2 可以知道,AbstractAutowireCapableBeanFactory
是 AbstractBeanFactory
的子类。
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
RootBeanDefinition mbdToUse = mbd;
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 如果可以提前暴露引用,则添加 getEarlyBeanReference 对象工厂到 singletonFactories 缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 填充 bean 的字段
// A 依赖 B,创建 A bean 的时候,会在这里创建 B bean
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 如果已经提前暴露过 bean 的引用,说明执行过 getEarlyBeanReference 方法。
// 且在 initializeBean 中没有改变过 bean 的引用,
// 则把 singletonObjects 将缓存的 bean 修改为之前提前暴露的引用。
exposedObject = earlySingletonReference;
}
}
}
// 返回的 exposedObject 会在 getSingleton 方法中通过 addSingleton 添加到 singletonObjects 缓存中
return exposedObject;
}
2.4 AbstractAutowireCapableBeanFactory
中的 getEarlyBeanReference
方法
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
// 执行 SmartInstantiationAwareBeanPostProcessor 的 getEarlyBeanReference 方法
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
2.5 Spring 容器中的 BeanPostProcessor
集合
图3:初始化 A 时的 BeanPostProcessor 集合 .png
我们仅关注 AnnotationAwareAspectJAutoProxyCreator
和 ScheduledAnnotationBeanPostProcessor
两个 BPP。
2.5.1 AnnotationAwareAspectJAutoProxyCreator
类继承关系图如下所示。
图4:AnnotationAwareAspectJAutoProxyCreator 继承关系.png关注其祖先 AbstractAutoProxyCreator
类中的 getEarlyBeanReference()
和 postProcessAfterInitialization()
方法。
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 添加到 earlyProxyReferences 缓存,避免重复创建代理对象。
this.earlyProxyReferences.put(cacheKey, bean);
// 生成代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 如果 earlyProxyReferences 缓存没有,才生成代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
可以发现 getEarlyBeanReference()
方法生成了代理对象后,会导致 postProcessAfterInitialization()
方法不会生成代理对象。
2.5.2 ScheduledAnnotationBeanPostProcessor
ScheduledAnnotationBeanPostProcessor
没有实现 SmartInstantiationAwareBeanPostProcessor
接口,故而没有 getEarlyBeanReference()
方法。
其 postProcessAfterInitialization
方法如下所示。
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler ||
bean instanceof ScheduledExecutorService) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
if (!this.nonAnnotatedClasses.contains(targetClass)) {
// 找到所有有 @Scheduled 注解的 Method
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
(MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
method, Scheduled.class, Schedules.class);
return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
});
// 分别调用 processScheduled 从而运行 ScheduledMethodRunnable 任务
annotatedMethods.forEach((method, scheduledMethods) ->
scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean)));
}
return bean;
}
2.5.3 AbstractAutowireCapableBeanFactory
的 applyBeanPostProcessorsAfterInitialization
方法
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
问题原因
循环引用导致 AnnotationAwareAspectJAutoProxyCreator
的getEarlyBeanReference()
方法生成了代理对象,从而在调用 postProcessAfterInitialization()
方法时不会生成代理对象。
进而导致 ScheduledAnnotationBeanPostProcessor
使用的 bean 实例是没有被代理的对象,所以不会被 ScheduleDisableAspect
切面拦截。
如果 A
依赖 B
,且 B
依赖 A
,先创建 A
的话,会导致 A
提前暴露 bean 引用,然后在 populateBean()
中会创建 B
从而获取到 A
提前暴露的 bean 引用,这时,A
已经通过 getEarlyBeanReference()
方法生成了代理对象。进而导致 A
在 postProcessAfterInitialization()
方法执行时不会生成代理对象,所以被 ScheduledMethodRunnable
管理的 A
对象不是代理对象,因此不会被 ScheduleDisableAspect
切面拦截。
结论
在 A
依赖 B
,且 B
依赖 A
的情况下。
- 先创建
A
,则A
中的@Scheduled
方法不会被ScheduleDisableAspect
切面拦截。 - 先创建
B
,则B
中的@Scheduled
方法不会被ScheduleDisableAspect
切面拦截。
问题拓展
通过 @DependsOn
注解可以控制 bean 的初始化顺序。
在类 A
上加上 @DependsOn("b")
注解,会让 B
先初始化。
@Slf4j
@Component
@DependsOn("b")
public class A {
@Autowired
private B b;
@Scheduled(fixedDelay = 1000)
public void testA() {
log.info("testA execute. b:{}", b);
}
}
运行 SpringDemoApplication
可以发现现象与我们推导的结论一致。
解决问题的办法
- 去掉
A
和B
中的@Scheduled
注解。 - 新增
ScheduledTask
类,用来执行A
和B
中的@Scheduled
任务。
具体代码如下所示:
@Slf4j
@Component
@DependsOn("b")
public class A {
@Autowired
private B b;
public void testA() {
log.info("testA execute. b:{}", b);
}
}
@Slf4j
@Component
public class B {
@Autowired
private A a;
public void testB() {
log.info("testB execute. a:{}", a);
}
}
@Component
public class ScheduledTask {
@Autowired
private A a;
@Autowired
private B b;
@Scheduled(fixedDelay = 1000)
public void testA() {
a.testA();
}
@Scheduled(fixedDelay = 1000)
public void testB() {
b.testB();
}
}
运行 SpringDemoApplication
可以发现 @Scheduled
都已经被拦截。
网友评论