Spring แก้ปัญหานี้อย่างไร: bean A ขึ้นอยู่กับ bean B และ bean B บน bean A
Spring แก้ปัญหานี้อย่างไร: bean A ขึ้นอยู่กับ bean B และ bean B บน bean A
คำตอบ:
ดังที่คำตอบอื่น ๆ ได้กล่าวไว้ว่า Spring เพียงแค่ดูแลมันสร้างเมล็ดถั่วและฉีดเข้าไปตามต้องการ
ผลที่ตามมาประการหนึ่งคือการตั้งค่า bean injection / property อาจเกิดขึ้นในลำดับที่แตกต่างจากสิ่งที่ไฟล์การเดินสาย XML ของคุณดูเหมือนจะบ่งบอก ดังนั้นคุณต้องระวังว่าตัวตั้งค่าคุณสมบัติของคุณไม่ได้ทำการเริ่มต้นที่อาศัยตัวตั้งค่าอื่นที่ถูกเรียกไปแล้ว วิธีจัดการกับปัญหานี้คือประกาศ bean ว่าใช้InitializingBean
อินเทอร์เฟซ สิ่งนี้ต้องการให้คุณใช้afterPropertiesSet()
วิธีนี้และนี่คือที่ที่คุณทำการเริ่มต้นที่สำคัญ (ฉันยังใส่รหัสเพื่อตรวจสอบว่ามีการตั้งค่าคุณสมบัติที่สำคัญจริง)
คู่มืออ้างอิงฤดูใบไม้ผลิอธิบายถึงวิธีการอ้างอิงแบบวงกลมได้รับการแก้ไข ถั่วจะถูกสร้างอินสแตนซ์ก่อนจากนั้นจึงฉีดเข้าไปในกันและกัน
พิจารณาคลาสนี้:
package mypackage;
public class A {
public A() {
System.out.println("Creating instance of A");
}
private B b;
public void setB(B b) {
System.out.println("Setting property b of A instance");
this.b = b;
}
}
และคลาสที่คล้ายกันB
:
package mypackage;
public class B {
public B() {
System.out.println("Creating instance of B");
}
private A a;
public void setA(A a) {
System.out.println("Setting property a of B instance");
this.a = a;
}
}
หากคุณมีไฟล์กำหนดค่านี้:
<bean id="a" class="mypackage.A">
<property name="b" ref="b" />
</bean>
<bean id="b" class="mypackage.B">
<property name="a" ref="a" />
</bean>
คุณจะเห็นผลลัพธ์ต่อไปนี้เมื่อสร้างบริบทโดยใช้การกำหนดค่านี้:
Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance
ทราบว่าเมื่อa
ถูกฉีดเข้าไปในb
, a
ยังไม่ได้เริ่มต้นใช้งานอย่างเต็มที่
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
ใน codebase ฉันกำลังใช้งาน (โค้ดมากกว่า 1 ล้านบรรทัด) เรามีปัญหากับเวลาเริ่มต้นที่ยาวนานประมาณ 60 วินาที เราได้รับมากกว่า 12000+ FactoryBeanNotInitializedException FactoryBeanNotInitializedException
สิ่งที่ฉันทำคือตั้งค่าเบรกพอยต์แบบมีเงื่อนไขในAbstractBeanFactory # doGetBean
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;
}
destroySingleton(beanName)
ฉันพิมพ์ข้อยกเว้นพร้อมรหัสเบรกพอยต์ตามเงื่อนไขที่ไหน:
System.out.println(ex);
return false;
เห็นได้ชัดว่าสิ่งนี้เกิดขึ้นเมื่อFactoryBeanมีส่วนร่วมในกราฟการพึ่งพาแบบวนรอบ เราแก้ไขได้โดยใช้ApplicationContextAwareและ InitializingBeanและฉีดถั่วด้วยตนเอง
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class A implements ApplicationContextAware, InitializingBean{
private B cyclicDepenency;
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
ctx = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
cyclicDepenency = ctx.getBean(B.class);
}
public void useCyclicDependency()
{
cyclicDepenency.doSomething();
}
}
ซึ่งจะลดเวลาในการเริ่มต้นลงเหลือประมาณ 15 วินาที
ดังนั้นอย่าคิดเสมอไปว่าฤดูใบไม้ผลิสามารถแก้ข้อมูลอ้างอิงเหล่านี้ให้คุณได้ดี
ด้วยเหตุนี้ฉันขอแนะนำให้ปิดใช้งานการแก้ปัญหาการอ้างอิงแบบวนรอบด้วยAbstractRefreshableApplicationContext # setAllowCircularReferences (false)เพื่อป้องกันปัญหาในอนาคตมากมาย
ปัญหา ->
Class A {
private final B b; // must initialize in ctor/instance block
public A(B b) { this.b = b };
}
Class B {
private final A a; // must initialize in ctor/instance block
public B(A a) { this.a = a };
}
// เกิดจาก: org.springframework.beans.factory BeanCurrentlyInCreationException: เกิดข้อผิดพลาดในการสร้าง bean ด้วยชื่อ 'A': Requested bean อยู่ระหว่างการสร้าง: มีการอ้างอิงแบบวงกลมที่ไม่สามารถแก้ไขได้หรือไม่?
โซลูชันที่ 1 ->
Class A {
private B b;
public A( ) { };
//getter-setter for B b
}
Class B {
private A a;
public B( ) { };
//getter-setter for A a
}
โซลูชันที่ 2 ->
Class A {
private final B b; // must initialize in ctor/instance block
public A(@Lazy B b) { this.b = b };
}
Class B {
private final A a; // must initialize in ctor/instance block
public B(A a) { this.a = a };
}
มันก็ทำมัน มันอินสแตนซ์a
และb
และฉีดแต่ละอันเข้าไปในอีกอันหนึ่ง (โดยใช้วิธีเซ็ตเตอร์)
มีปัญหาอะไร?
จากSpring Reference :
โดยทั่วไปคุณสามารถวางใจให้ Spring ทำในสิ่งที่ถูกต้องได้ ตรวจพบปัญหาการกำหนดค่าเช่นการอ้างอิงถึงถั่วที่ไม่มีอยู่จริงและการอ้างอิงแบบวงกลมในเวลาโหลดคอนเทนเนอร์ Spring กำหนดคุณสมบัติและแก้ไขการอ้างอิงให้ช้าที่สุดเมื่อสร้าง bean ขึ้นมาจริงๆ
Spring container สามารถแก้ปัญหาการอ้างอิงแบบวงกลมที่ใช้ Setter แต่ให้ข้อยกเว้นรันไทม์ BeanCurrentlyInCreationException ในกรณีของการอ้างอิงแบบวงกลมที่ใช้ตัวสร้าง ในกรณีของการพึ่งพาแบบวงกลมตาม Setter คอนเทนเนอร์ IOC จะจัดการกับมันแตกต่างจากสถานการณ์ทั่วไปซึ่งจะกำหนดค่า bean การทำงานร่วมกันอย่างสมบูรณ์ก่อนที่จะฉีด ตัวอย่างเช่นถ้า Bean A ขึ้นอยู่กับ Bean B และ Bean B บน Bean C คอนเทนเนอร์จะเริ่มต้น C อย่างสมบูรณ์ก่อนที่จะฉีดไปที่ B และเมื่อ B เริ่มต้นอย่างสมบูรณ์แล้วจะถูกฉีดไปที่ A แต่ในกรณีของการพึ่งพาแบบวงกลมหนึ่ง ของถั่วจะถูกฉีดไปที่อื่นก่อนที่จะเริ่มต้นอย่างสมบูรณ์
พูดว่า A ขึ้นอยู่กับ B จากนั้น Spring จะสร้างอินสแตนซ์ A ก่อนตามด้วย B จากนั้นตั้งค่าคุณสมบัติสำหรับ B จากนั้นตั้งค่า B เป็น A
แต่ถ้า B ขึ้นอยู่กับ A ล่ะ?
ความเข้าใจของฉันคือ: ฤดูใบไม้ผลิเพิ่งพบว่า A ถูกสร้างขึ้น (ตัวสร้างดำเนินการ) แต่ไม่ได้เริ่มต้นอย่างสมบูรณ์ (ไม่ใช่การฉีดทั้งหมดที่เสร็จสิ้น) คิดว่ามันก็โอเคเป็นที่ยอมรับได้ว่า A ไม่ได้เริ่มต้นอย่างสมบูรณ์เพียงแค่ตั้งค่านี้ไม่ใช่ - เริ่มต้นอย่างสมบูรณ์อินสแตนซ์ A เป็น B ในตอนนี้ หลังจาก B เริ่มต้นอย่างสมบูรณ์แล้วจะถูกตั้งค่าเป็น A และในที่สุด A ก็เริ่มต้นอย่างสมบูรณ์ในขณะนี้
กล่าวอีกนัยหนึ่งก็คือแสดง A ถึง B ล่วงหน้า
สำหรับการอ้างอิงผ่านตัวสร้าง Sprint เพียงแค่โยน BeanCurrentlyInCreationException เพื่อแก้ไขข้อยกเว้นนี้ตั้งค่า lazy-init เป็น true สำหรับ bean ซึ่งขึ้นอยู่กับผู้อื่นผ่านทาง constructor-arg
หากโดยทั่วไปคุณใช้ constructor-injection และไม่ต้องการเปลี่ยนไปใช้ property-injection การค้นหาวิธีการ - การฉีดของ Spring จะช่วยให้ถั่วหนึ่งตัวค้นหาอีกอันหนึ่งอย่างเกียจคร้านและด้วยเหตุนี้วิธีแก้ปัญหาการพึ่งพาแบบวัฏจักร ดูที่นี่: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
Constructor Injection ล้มเหลวเมื่อมี Circular Dependency ระหว่างถั่วสปริง ดังนั้นในกรณีนี้การฉีด Setter จะช่วยแก้ไขปัญหา
โดยพื้นฐานแล้ว Constructor Injection มีประโยชน์สำหรับการพึ่งพาที่จำเป็นสำหรับการพึ่งพาทางเลือกที่ดีกว่าที่จะใช้ Setter injection เนื่องจากเราสามารถทำการฉีดซ้ำได้
หากถั่วสองเมล็ดขึ้นอยู่กับกันและกันเราไม่ควรใช้ Constructor injection ในทั้งสองคำจำกัดความของถั่ว เราต้องใช้ setter injection ในถั่วชนิดใดชนิดหนึ่งแทน (แน่นอนว่าเราสามารถใช้ setter injection n ทั้งคำจำกัดความของ bean แต่การฉีดตัวสร้างในทั้งสองโยน 'BeanCurrentlyInCreationException'
อ้างอิง Spring doc ที่ " https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource "