Article Directory
This chapter we mainly talk about the
doCreateBean()
process, how `spring is to resolve circular dependencies, and some use the wrong way.
problem
Suppose now that we have two classes, namely, A
class, B
class, and A
class need to refer to B
the class, B
the class need to refer to A
the class, which is a set of baby mode.
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
So how Spring handle this situation?
Here we revisit spring-loaded process for the bean.
solution
In fact, out in front of the loading process solution is very simple, as long as 填充Bean属性
between the directly into BeanFactory
the can, so back to the current dependence bean can be taken directly from the BeanFactory, and then return to go out, do not go in creation bean process.
Here we look at the perspective of Bean is how to solve.
This jumped out of the cycle of death has been created.
Non-normal performance
Read the above solutions whether they think it'll be safe? You can rest assured the bold circular dependencies Bean?
Here we look at an example, it is based on the above two categories, only some small change.
@Component
public class A {
@Autowired
private B b;
@Async
public void async(){}
}
@Component
public class B {
@Autowired
private A a;
@Async
public void async(){}
}
We're just a way to add an annotation @Async
, then the situation will be changed dramatically.
Then given a circular dependencies, wherein the bean warpper used.
Not to say that the good has been solved circular dependencies thing? How reported this error?
This @Async
annotation in the end is what demons.
We first take a look at this place which is wrong in the end thrown out.
The following code occurs after Bean created.
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
Or go step by step
- The first step to get through to the original Bean BeanFactory
- The second step, Bean and Bean retrieved to determine whether the creation of the same (important ... some students hitting on yet?), The same as the direct return Bean created
- The third step, if allowed to inject wrappingBean current internal and Bean have dependence, and dependence has been used, it throws the above error.
If the general case, we will direct return in the second step, since the original Bean and Bean we have created is exactly the same. (That is, no @Async
comment when the idea came up thing?)
I thought then, in turn, use the @Async
annotation will lead us to create the Bean and from BeanFactory
inconsistencies in to get the original Bean.
why?
First @Async
annotation function is to convert the synchronous operation of the asynchronous operation, then how to achieve it without intrusion? Acting right. so, Bean to create a proxy class is returned. It is not the same.
So long as the annotations that could be proxied Bean, it will cause this problem. (Circular dependencies)
solution
So how to solve it?
@Lazy
Notes, since the spring @Lazy
will determine the conditions can not be reached and skip detection.
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName))
That is, this.allowRawInjectionDespiteWrapping
the value becomes true.
Because the @Lazy
notes is returned proxy Bean, so let this Spring Bean skip this test.
Example:
@Component
public class A {
@Autowired
@Lazy
private B b;
@Async
public void async(){}
}
@Component
public class B {
@Autowired
@Lazy
private A a;
@Async
public void async(){}
}