欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring循環(huán)依賴的解決辦法,你真的懂了嗎

 更新時(shí)間:2020年06月23日 09:26:00   作者:Java識(shí)堂  
循壞依賴即循環(huán)引用,兩個(gè)或多個(gè)bean相互引用,最終形成一個(gè)環(huán)。這篇文章主要介紹了Spring循環(huán)依賴的解決辦法,需要的朋友可以參考下

介紹

先說(shuō)一下什么是循環(huán)依賴,循壞依賴即循環(huán)引用,兩個(gè)或多個(gè)bean相互引用,最終形成一個(gè)環(huán)。Spring在初始化A的時(shí)候需要注入B,而初始化B的時(shí)候需要注入A,在Spring啟動(dòng)后這2個(gè)Bean都要被初始化完成

Spring的循環(huán)依賴有兩種場(chǎng)景

  • 構(gòu)造器的循環(huán)依賴
  • 屬性的循環(huán)依賴

構(gòu)造器的循環(huán)依賴,可以在構(gòu)造函數(shù)中使用@Lazy注解延遲加載。在注入依賴時(shí),先注入代理對(duì)象,當(dāng)首次使用時(shí)再創(chuàng)建對(duì)象完成注入

屬性的循環(huán)依賴主要是通過(guò)3個(gè)map來(lái)解決的

構(gòu)造器的循環(huán)依賴

@Component
public class ConstructorA {

 private ConstructorB constructorB;

 @Autowired
 public ConstructorA(ConstructorB constructorB) {
 this.constructorB = constructorB;
 }
}
@Component
public class ConstructorB {

 private ConstructorA constructorA;

 @Autowired
 public ConstructorB(ConstructorA constructorA) {
 this.constructorA = constructorA;
 }
}
@Configuration
@ComponentScan("com.javashitang.dependency.constructor")
public class ConstructorConfig {
}
public class ConstructorMain {

 public static void main(String[] args) {
 AnnotationConfigApplicationContext context =
 new AnnotationConfigApplicationContext(ConstructorConfig.class);
 System.out.println(context.getBean(ConstructorA.class));
 System.out.println(context.getBean(ConstructorB.class));
 }
}

運(yùn)行ConstructorMain的main方法的時(shí)候會(huì)在第一行就報(bào)異常,說(shuō)明Spring沒(méi)辦法初始化所有的Bean,即上面這種形式的循環(huán)依賴Spring無(wú)法解決。

我們可以在ConstructorA或者ConstructorB構(gòu)造函數(shù)的參數(shù)上加上@Lazy注解就可以解決

@Autowired
public ConstructorB(@Lazy ConstructorA constructorA) {
	this.constructorA = constructorA;
}

因?yàn)槲覀冎饕P(guān)注屬性的循環(huán)依賴,構(gòu)造器的循環(huán)依賴就不做過(guò)多分析了

屬性的循環(huán)依賴

先演示一下什么是屬性的循環(huán)依賴

@Component
public class FieldA {

 @Autowired
 private FieldB fieldB;
}
@Component
public class FieldB {

 @Autowired
 private FieldA fieldA;
}
@Configuration
@ComponentScan("com.javashitang.dependency.field")
public class FieldConfig {
}
public class FieldMain {

 public static void main(String[] args) {
 AnnotationConfigApplicationContext context =
 new AnnotationConfigApplicationContext(FieldConfig.class);
 // com.javashitang.dependency.field.FieldA@3aa9e816
 System.out.println(context.getBean(FieldA.class));
 // com.javashitang.dependency.field.FieldB@17d99928
 System.out.println(context.getBean(FieldB.class));
 }
}

Spring容器正常啟動(dòng),能獲取到FieldA和FieldB這2個(gè)Bean

屬性的循環(huán)依賴在面試中還是經(jīng)常被問(wèn)到的。總體來(lái)說(shuō)也不復(fù)雜,但是涉及到Spring Bean的初始化過(guò)程,所以感覺(jué)比較復(fù)雜,我寫(xiě)個(gè)demo演示一下整個(gè)過(guò)程

Spring的Bean的初始化過(guò)程其實(shí)比較復(fù)雜,為了方便理解Demo,我就把Spring Bean的初始化過(guò)程分為2部分

  • bean的實(shí)例化過(guò)程,即調(diào)用構(gòu)造函數(shù)將對(duì)象創(chuàng)建出來(lái)
  • bean的初始化過(guò)程,即填充bean的各種屬性

bean初始化過(guò)程完畢,則bean就能被正常創(chuàng)建出來(lái)了

下面開(kāi)始寫(xiě)Demo,ObjectFactory接口用來(lái)生產(chǎn)Bean,和Spring中定義的接口一樣

public interface ObjectFactory<T> {
 T getObject();
}

public class DependencyDemo {

 // 初始化完畢的Bean
 private final Map<String, Object> singletonObjects =
 new ConcurrentHashMap<>(256);

 // 正在初始化的Bean對(duì)應(yīng)的工廠,此時(shí)對(duì)象已經(jīng)被實(shí)例化
 private final Map<String, ObjectFactory<?>> singletonFactories =
 new HashMap<>(16);

 // 存放正在初始化的Bean,對(duì)象還沒(méi)有被實(shí)例化之前就放進(jìn)來(lái)了
 private final Set<String> singletonsCurrentlyInCreation =
 Collections.newSetFromMap(new ConcurrentHashMap<>(16));

 public <T> T getBean(Class<T> beanClass) throws Exception {
 // 類名為Bean的名字
 String beanName = beanClass.getSimpleName();
 // 已經(jīng)初始化好了,或者正在初始化
 Object initObj = getSingleton(beanName, true);
 if (initObj != null) {
 return (T) initObj;
 }
 // bean正在被初始化
 singletonsCurrentlyInCreation.add(beanName);
 // 實(shí)例化bean
 Object object = beanClass.getDeclaredConstructor().newInstance();
 singletonFactories.put(beanName, () -> {
 return object;
 });
 // 開(kāi)始初始化bean,即填充屬性
 Field[] fields = object.getClass().getDeclaredFields();
 for (Field field : fields) {
 field.setAccessible(true);
 // 獲取需要注入字段的class
 Class<?> fieldClass = field.getType();
 field.set(object, getBean(fieldClass));
 }
 // 初始化完畢
 singletonObjects.put(beanName, object);
 singletonsCurrentlyInCreation.remove(beanName);
 return (T) object;
 }

 /**
 * allowEarlyReference參數(shù)的含義是Spring是否允許循環(huán)依賴,默認(rèn)為true
 * 所以當(dāng)allowEarlyReference設(shè)置為false的時(shí)候,當(dāng)項(xiàng)目存在循環(huán)依賴,會(huì)啟動(dòng)失敗
 */
 public Object getSingleton(String beanName, boolean allowEarlyReference) {
 Object singletonObject = this.singletonObjects.get(beanName);
 if (singletonObject == null 
 && isSingletonCurrentlyInCreation(beanName)) {
 synchronized (this.singletonObjects) {
 if (singletonObject == null && allowEarlyReference) {
  ObjectFactory<?> singletonFactory =
  this.singletonFactories.get(beanName);
  if (singletonFactory != null) {
  singletonObject = singletonFactory.getObject();
  }
 }
 }
 }
 return singletonObject;
 }

 /**
 * 判斷bean是否正在被初始化
 */
 public boolean isSingletonCurrentlyInCreation(String beanName) {
 return this.singletonsCurrentlyInCreation.contains(beanName);
 }

}

測(cè)試一波

public static void main(String[] args) throws Exception {
	DependencyDemo dependencyDemo = new DependencyDemo();
	// 假裝掃描出來(lái)的對(duì)象
	Class[] classes = {A.class, B.class};
	// 假裝項(xiàng)目初始化所有bean
	for (Class aClass : classes) {
		dependencyDemo.getBean(aClass);
	}
	// true
	System.out.println(
			dependencyDemo.getBean(B.class).getA() == dependencyDemo.getBean(A.class));
	// true
	System.out.println(
			dependencyDemo.getBean(A.class).getB() == dependencyDemo.getBean(B.class));
}

是不是很簡(jiǎn)單?我們只用了2個(gè)map就搞定了Spring的循環(huán)依賴

2個(gè)Map就能搞定循環(huán)依賴,那為什么Spring要用3個(gè)Map呢?

原因其實(shí)也很簡(jiǎn)單,當(dāng)我們從singletonFactories中根據(jù)BeanName獲取相應(yīng)的ObjectFactory,然后調(diào)用getObject()這個(gè)方法返回對(duì)應(yīng)的Bean。在我們的例子中
ObjectFactory的實(shí)現(xiàn)很簡(jiǎn)單哈,就是將實(shí)例化好的對(duì)象直接返回,但是在Spring中就沒(méi)有這么簡(jiǎn)單了,執(zhí)行過(guò)程比較復(fù)雜,為了避免每次拿到ObjectFactory然后調(diào)用getObject(),我們直接把ObjectFactory創(chuàng)建的對(duì)象緩存起來(lái)不就行了,這樣就能提高效率了

比如A依賴B和C,B和C又依賴A,如果不做緩存那么初始化B和C都會(huì)調(diào)用A對(duì)應(yīng)的ObjectFactory的getObject()方法。如果做緩存只需要B或者C調(diào)用一次即可。

知道了思路,我們把上面的代碼改一波,加個(gè)緩存。

public class DependencyDemo {

	// 初始化完畢的Bean
	private final Map<String, Object> singletonObjects =
			new ConcurrentHashMap<>(256);

	// 正在初始化的Bean對(duì)應(yīng)的工廠,此時(shí)對(duì)象已經(jīng)被實(shí)例化
	private final Map<String, ObjectFactory<?>> singletonFactories =
			new HashMap<>(16);

	// 緩存Bean對(duì)應(yīng)的工廠生產(chǎn)好的Bean
	private final Map<String, Object> earlySingletonObjects =
			new HashMap<>(16);

	// 存放正在初始化的Bean,對(duì)象還沒(méi)有被實(shí)例化之前就放進(jìn)來(lái)了
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	public <T> T getBean(Class<T> beanClass) throws Exception {
		// 類名為Bean的名字
		String beanName = beanClass.getSimpleName();
		// 已經(jīng)初始化好了,或者正在初始化
		Object initObj = getSingleton(beanName, true);
		if (initObj != null) {
			return (T) initObj;
		}
		// bean正在被初始化
		singletonsCurrentlyInCreation.add(beanName);
		// 實(shí)例化bean
		Object object = beanClass.getDeclaredConstructor().newInstance();
		singletonFactories.put(beanName, () -> {
			return object;
		});
		// 開(kāi)始初始化bean,即填充屬性
		Field[] fields = object.getClass().getDeclaredFields();
		for (Field field : fields) {
			field.setAccessible(true);
			// 獲取需要注入字段的class
			Class<?> fieldClass = field.getType();
			field.set(object, getBean(fieldClass));
		}
		singletonObjects.put(beanName, object);
		singletonsCurrentlyInCreation.remove(beanName);
		earlySingletonObjects.remove(beanName);
		return (T) object;
	}

	/**
	 * allowEarlyReference參數(shù)的含義是Spring是否允許循環(huán)依賴,默認(rèn)為true
	 */
	public 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;
	}
}

我們寫(xiě)的getSingleton的實(shí)現(xiàn)和org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)的實(shí)現(xiàn)一模一樣,這個(gè)方法幾乎所有分析Spring循環(huán)依賴的文章都會(huì)提到,這次你明白工作原理是什么了把

總結(jié)一波

  • 拿bean的時(shí)候先從singletonObjects(一級(jí)緩存)中獲取
  • 如果獲取不到,并且對(duì)象正在創(chuàng)建中,就從earlySingletonObjects(二級(jí)緩存)中獲取
  • 如果還是獲取不到就從singletonFactories(三級(jí)緩存)中獲取,然后將獲取到的對(duì)象放到earlySingletonObjects(二級(jí)緩存)中,并且將bean對(duì)應(yīng)的singletonFactories(三級(jí)緩存)清除
  • bean初始化完畢,放到singletonObjects(一級(jí)緩存)中,將bean對(duì)應(yīng)的earlySingletonObjects(二級(jí)緩存)清除

參考博客

[1]https://mp.weixin.qq.com/s/gBr3UfC1HRcw4U-ZMmtRaQ
[2]https://mp.weixin.qq.com/s/5mwkgJB7GyLdKDgzijyvXw
比較詳細(xì)
[1]https://zhuanlan.zhihu.com/p/84267654
[2]https://juejin.im/post/5c98a7b4f265da60ee12e9b2

到此這篇關(guān)于Spring循環(huán)依賴的解決辦法,你真的懂了嗎的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論