我们知道当通过构造方法的方式注入属性时,是不支持循环依赖这种场景的,本文主要通过分析源码看看为什么构造方法不能支持循环依赖。
当然,如果读者还不了解循环依赖的问题,建议先结合源码搞清楚,可以先看看这篇文章,
深入源码分析Spring如何解决循环依赖,否则很有可能看不懂本文内容。
先看问题
@Component
public class TestA {
private final TestB testB;
public TestA(TestB testB) {
this.testB = testB;
}
}
@Component
public class TestB {
private final TestA testA;
public TestB(TestA testA) {
this.testA = testA;
}
}
TestA和TestB通过构造方法互相注入依赖,结果启动报错咯!
进入源码分析
假设先准备实例化TestA对象,直接找到实例化关键代码部分。
//省略部分源码。。。
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//省略部分源码。。。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
//这个方法发挥了重要的作用
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//调用createBean方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
beforeSingletonCreation,上编文章分析过,把当前bean放到一个set集合中,错误就是从这抛出来的,也就是放入set集合失败了,稍微我们再来分析,第一次肯定是没问题的。
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
createBean方法
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//省略部分源码。。。
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
//省略部分源码。。。
}
doCreateBean 这个是核心方法,本文就关注实例化这个方法,在非构造方法依赖的情况下,这个方法是通过反射调用无参构造方法实例化的,而现在不行了。
当Spring发现有autowiring的构造函数,就会通过这个构造函数进行实例化。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//省略部分源码。。。
if (instanceWrapper == null) {
//实例化
instanceWrapper = createBeanInstance(beanName, mbd, args);
//省略部分源码。。。
}
}
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
//省略部分源码。。。
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
//有参调用这个方法
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
//无参构造函数是调用这个方法
return instantiateBean(beanName, mbd);
}
autowireConstructor
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
//省略部分源码。。。
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
//省略部分源码。。。
}
createArgumentArray
private ArgumentsHolder createArgumentArray(
String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
//省略部分源码。。。
try {
Object autowiredArgument = resolveAutowiredArgument(
methodParam, beanName, autowiredBeanNames, converter, fallback);
args.rawArguments[paramIndex] = autowiredArgument;
args.arguments[paramIndex] = autowiredArgument;
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
args.resolveNecessary = true;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
}
//省略部分源码。。。
}
resolveAutowiredArgument
@Nullable
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
//省略部分源码。。。
try {
return this.beanFactory.resolveDependency(
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
}
catch (NoUniqueBeanDefinitionException ex) {
throw ex;
}
//省略部分源码。。。
}
resolveDependency这个方法在 深入源码分析Spring如何解决循环依赖 文中有看过,最终会调用到beanName的getBean方法。
源码看到这边已经足够了,我们可以看出与无参构造函数不同之处在于,依赖属性的getBean调用提前了,无参构造函数是在类实例化完成,并且放入到了三级缓存中之后才调用的,而现在不是了,也就是说现在缓存中并没有TestA。
所以当调用getBean(testB)时,同样会走到getBean(testA),此时缓存中没有testA,那么就又会反复执行testA的实例化过程,那么是在什么地方终止的呢?
就是这个方法了,此时testA要想再加入这个set集合就会返回false了,因为第一次testA的实例化过程就已经放入这个set集合了,所以此处就抛出了异常,终止了整个流程。
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
构造方式依赖注入
@Autowired属性依赖注入
总结
通过构造方法之所以失败的主要原因就是,依赖的属性被提前实例化了,因为testA未完成实例化,内存中也就不存在有关testA的任何记录,自然缓存中也没有,所以当再次触发testA的实例化时,也没法从缓存中获取,最终通过一个set集合判断是否被重复添加来控制,避免陷入死循环。。。