spring中bean的生命周期
spring bean的作用域
spring 如何解決循環(huán)依賴
@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.是典型的循環(huán)依賴的情況酌壕,但是運行的結果來看,程序能正常運行歇由,調用a.aMethod()能打印出"bmethod"字符串
那spring是怎么解決循環(huán)依賴的呢卵牍??沦泌?
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);
}
.......
各種作用域的創(chuàng)建bean
}
獲取bean的時候
- 從spring維護的三個緩存中獲取bean
- 如果緩存中獲取失敗糊昙,并且bean處于創(chuàng)建中,拋出循環(huán)依賴異常
- 在對應的作用域中創(chuàng)建bean
spring為bean創(chuàng)建了三個級別的緩存谢谦,
這三級緩存分別是
級別 | 名稱 | 類型 | 內容 |
---|---|---|---|
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的第一步操作就是從三個級別緩存中獲取bean
- 如果singletonObjects中有萝衩,可以直接返回
- 如果earlySingletonObjects中有,直接返回
- 如果singletonFactories中有
- 調用ObjectFactory的getObject方法
- 將獲取到的bean放入二級緩存中
- 從三級緩存中刪除該beanName
在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執(zhí)行populateBean填充屬性的時候,因為A已經加入到三級緩存中了镜撩,所以在getBean的最開始就能從緩存中獲取到已經完成實例化的beanA,完成beanB的屬性填充预柒。
什么情況下的循環(huán)依賴問題可以被spring解決
首先,我們常用的依賴注入方式有如下幾種
- 構造函數注入
@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();
}
}
運行結果:報循環(huán)依賴異常
***************************
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");
}
}
運行正常
按照前面的代碼分析袁梗,spring會在完成實例化createBeanInstance方法執(zhí)行完成后宜鸯,才會把bean提前曝光到緩存中,才能讓依賴方獲取到這個提前曝光的bean完成依賴方bean的加載遮怜。如果將依賴寫在構造函數中淋袖,會在實例化C的時候就去加載依賴的D,在D加載過程中也會在他的構造函數中去加載C的bean,C當前處于創(chuàng)建中锯梁,但是沒有進行提前曝光即碗,導致循環(huán)依賴異常
如果將構造器注入的示例改成如下:
@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之前,能把自己提前曝光出去陌凳,就不過報循環(huán)依賴了剥懒。
earlySingletonObjects和singletonFactories
在doCreateBean方法中,完成bean的實例化后合敦,如果允許提前曝光初橘,會把剛剛完成實例化的bean暴露出去
此時加入到三級緩存緩存中。目的是為了在調用getSingleton從三級緩存升級到二級緩存中的時候能執(zhí)行一些拓展的操作
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;
}