@Component
public class A {
@Autowired
private B b;
public void aMethod(){
b.bMethod();
}
}
@Component
public class B {
@Autowired
private A a;
public void bMethod(){
System.out.println("bmethod");
}
}
@SpringBootApplication
public class MyspringlearningApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MyspringlearningApplication.class, args);
A a = context.getBean(A.class);
a.aMethod();
}
}
复制代码
上面的示例中,A依赖B,B又依赖了A.是典型的循环依赖的状况,可是运行的结果来看,程序能正常运行,调用a.aMethod()能打印出"bmethod"字符串
那spring是怎么解决循环依赖的呢???spring
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;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance: // We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
.......
各类做用域的建立bean
}
复制代码
获取bean的时候缓存
spring为bean建立了三个级别的缓存, 这三级缓存分别是bash
级别 | 名称 | 类型 | 内容 |
---|---|---|---|
1 | singletonObjects | Map<String, Object> | 正在完成加载的bean |
2 | earlySingletonObjects | Map<String, Object> | 完成了实例化,提早曝光的Bean02 |
3 | singletonFactories | Map<String, ObjectFactory<?>> | 提早曝光的bean01 |
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
复制代码
getBean的第一步操做就是从三个级别缓存中获取beanapp
在bean完成实例化以后,populateBean以前,会将bean提早曝光。加入到三级缓存中函数
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
.....
createBeanInstance
// 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) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
....
populateBean()
....
}
复制代码
当对beanB执行populateBean填充属性的时候,由于A已经加入到三级缓存中了,因此在getBean的最开始就能从缓存中获取到已经完成实例化的beanA,完成beanB的属性填充。ui
首先,咱们经常使用的依赖注入方式有以下几种this
@Component
public class C {
private D d;
public C(@Autowired D d) {
}
public void cMethod(){
d.dMethod();
}
}
@Component
public class D {
private C c;
public D(@Autowired C c) {
this.c = c;
}
public void dMethod(){
System.out.println("dMethod");
}
}
@SpringBootApplication
public class MyspringlearningApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MyspringlearningApplication.class, args);
C c = context.getBean(C.class);
c.cMethod();
}
}
复制代码
运行结果:报循环依赖异常spa
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| c defined in file [/Users/lihongli/testWorkSpace/myspringlearning/target/classes/com/li/myspringlearning/C.class]
↑ ↓
| d defined in file [/Users/lihongli/testWorkSpace/myspringlearning/target/classes/com/li/myspringlearning/D.class]
└─────┘
复制代码
@Component
public class A {
@Autowired
private B b;
public void aMethod(){
b.bMethod();
}
}
@Component
public class B {
@Autowired
private A a;
public void bMethod(){
System.out.println("bmethod");
}
}
复制代码
运行正常debug
按照前面的代码分析,spring会在完成实例化createBeanInstance方法执行完成后,才会把bean提早曝光到缓存中,才能让依赖方获取到这个提早曝光的bean完成依赖方bean的加载。若是将依赖写在构造函数中,会在实例化C的时候就去加载依赖的D,在D加载过程当中也会在他的构造函数中去加载C的bean,C当前处于建立中,可是没有进行提早曝光,致使循环依赖异常code
若是将构造器注入的示例改为以下:
@Component
public class C {
@Autowired
private D d;
// public C(@Autowired D d) {
// }
public void cMethod(){
d.dMethod();
}
}
@Component
public class D {
private C c;
public D(@Autowired C c) {
this.c = c;
}
public void dMethod(){
System.out.println("dMethod");
}
}
@SpringBootApplication
public class MyspringlearningApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MyspringlearningApplication.class, args);
C c = context.getBean(C.class);
c.cMethod();
}
}
复制代码
让C在获取D的bean以前,能把本身提早曝光出去,就不过报循环依赖了。
在doCreateBean方法中,完成bean的实例化后,若是容许提早曝光,会把刚刚完成实例化的bean暴露出去
此时加入到三级缓存缓存中。目的是为了在调用getSingleton从三级缓存升级到二级缓存中的时候能执行一些拓展的操做
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
复制代码