Spring giải quyết điều này như thế nào: bean A phụ thuộc vào bean B và bean B phụ thuộc vào bean A.
Spring giải quyết điều này như thế nào: bean A phụ thuộc vào bean B và bean B phụ thuộc vào bean A.
Câu trả lời:
Như các câu trả lời khác đã nói, Spring chỉ việc chăm sóc, tạo hạt đậu và tiêm theo yêu cầu.
Một trong những hậu quả là cài đặt đặc tính / chèn thêm bean có thể xảy ra theo một thứ tự khác với những gì mà các tệp nối dây XML của bạn dường như ngụ ý. Vì vậy, bạn cần phải cẩn thận rằng các bộ thiết lập thuộc tính của bạn không thực hiện khởi tạo mà dựa vào các bộ thiết lập khác đã được gọi. Cách để giải quyết vấn đề này là khai báo bean như việc triển khai InitializingBean
giao diện. Điều này yêu cầu bạn triển khai afterPropertiesSet()
phương thức và đây là nơi bạn thực hiện quá trình khởi tạo quan trọng. (Tôi cũng bao gồm mã để kiểm tra xem các thuộc tính quan trọng đã thực sự được thiết lập chưa.)
Các tài liệu tham khảo của nhãn hiệu Xuân giải thích như thế nào phụ thuộc vòng tròn được giải quyết. Các hạt đậu được làm liền trước, sau đó được tiêm vào nhau.
Hãy xem xét lớp học này:
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;
}
}
Và một lớp tương tự 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;
}
}
Nếu sau đó bạn có tệp cấu hình này:
<bean id="a" class="mypackage.A">
<property name="b" ref="b" />
</bean>
<bean id="b" class="mypackage.B">
<property name="a" ref="a" />
</bean>
Bạn sẽ thấy kết quả sau khi tạo ngữ cảnh bằng cấu hình này:
Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance
Lưu ý rằng khi a
được tiêm vào b
, a
vẫn chưa được khởi tạo hoàn toàn.
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Trong cơ sở mã mà tôi đang làm việc (hơn 1 triệu dòng mã), chúng tôi gặp sự cố với thời gian khởi động lâu, khoảng 60 giây. Chúng tôi đã nhận được hơn 12000 FactoryBeanNotInitializedException .
Những gì tôi đã làm là đặt một điểm ngắt có điều kiện trong 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;
}
nơi destroySingleton(beanName)
tôi đã in ngoại lệ với mã điểm ngắt có điều kiện:
System.out.println(ex);
return false;
Rõ ràng điều này xảy ra khi các FactoryBean tham gia vào một đồ thị phụ thuộc theo chu kỳ. Chúng tôi đã giải quyết nó bằng cách triển khai ApplicationContextAware và InitializingBean và tiêm các bean theo cách thủ công.
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();
}
}
Điều này làm giảm thời gian khởi động xuống còn khoảng 15 giây.
Vì vậy, đừng luôn cho rằng mùa xuân có thể giải quyết tốt các tài liệu tham khảo này cho bạn.
Vì lý do này, tôi khuyên bạn nên tắt tính năng phân giải phụ thuộc theo chu kỳ với AbstractRefreshableApplicationContext # setAllowCircularRefferences (false) để tránh nhiều sự cố trong tương lai.
Vấn đề ->
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 };
}
// Gây ra bởi: org.springframework.beans.factory.BeanCurrentlyInCreationException: Lỗi khi tạo bean với tên 'A': bean được yêu cầu hiện đang được tạo: Có tham chiếu vòng tròn không thể giải quyết được không?
Giải pháp 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
}
Giải pháp 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 };
}
Nó chỉ làm điều đó. Nó khởi tạo a
và b
và đưa từng cái vào cái kia (sử dụng các phương thức setter của chúng).
Vấn đề là gì?
Từ tham khảo mùa xuân :
Nhìn chung, bạn có thể tin tưởng Spring sẽ làm điều đúng đắn. Nó phát hiện các vấn đề về cấu hình, chẳng hạn như các tham chiếu đến các bean không tồn tại và các phụ thuộc vòng tròn, tại thời gian tải của vùng chứa. Spring thiết lập các thuộc tính và giải quyết các phụ thuộc càng muộn càng tốt, khi bean thực sự được tạo.
Vùng chứa Spring có thể giải quyết các phụ thuộc vòng tròn dựa trên Setter nhưng đưa ra một ngoại lệ thời gian chạy BeanCurrentlyInCreationException trong trường hợp phụ thuộc vòng tròn dựa trên Constructor. Trong trường hợp phụ thuộc vòng tròn dựa trên Setter, vùng chứa IOC xử lý nó khác với một tình huống điển hình trong đó nó sẽ cấu hình đầy đủ bean cộng tác trước khi đưa nó vào. Ví dụ: nếu Bean A có phụ thuộc vào Bean B và Bean B trên Bean C, vùng chứa khởi tạo đầy đủ C trước khi tiêm nó vào B và khi B được khởi tạo hoàn toàn, nó sẽ được đưa vào A. Nhưng trong trường hợp phụ thuộc vòng tròn, một của hạt đậu được tiêm cho hạt đậu kia trước khi nó được khởi chạy hoàn toàn.
Giả sử A phụ thuộc vào B, thì trước tiên Spring sẽ khởi tạo A, sau đó là B, sau đó đặt thuộc tính cho B, sau đó đặt B thành A.
Nhưng nếu B cũng phụ thuộc vào A thì sao?
Sự hiểu biết của tôi là: Spring chỉ thấy rằng A đã được xây dựng (phương thức khởi tạo được thực thi), nhưng chưa được khởi tạo hoàn toàn (không phải tất cả các lần tiêm đều được thực hiện), tốt, nó nghĩ, không sao cả, có thể chấp nhận được rằng A chưa được khởi tạo hoàn toàn, chỉ cần đặt điều này không- các cá thể A được khởi tạo đầy đủ thành B ngay bây giờ. Sau khi B được khởi tạo hoàn toàn, nó được đặt thành A, và cuối cùng, A đã được khởi tạo hoàn toàn ngay bây giờ.
Nói cách khác, nó chỉ phơi bày trước A với B.
Đối với các phụ thuộc thông qua phương thức khởi dựng, Sprint chỉ cần ném BeanCurrentlyInCreationException, để giải quyết ngoại lệ này, hãy đặt lazy-init thành true cho bean phụ thuộc vào những người khác thông qua cách khởi tạo-arg.
Nếu bạn thường sử dụng constructor-injection và không muốn chuyển sang thuộc tính-injection thì phương thức tra cứu -injection của Spring sẽ cho phép một bean lười biếng tra cứu bean kia và do đó giải quyết sự phụ thuộc theo chu kỳ. Xem tại đây: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
Constructor Injection không thành công khi có Sự phụ thuộc vòng giữa các hạt đậu lò xo. Vì vậy, trong trường hợp này, chúng tôi chèn Setter giúp giải quyết vấn đề.
Về cơ bản, Constructor Injection hữu ích cho các phụ thuộc Bắt buộc, đối với các phụ thuộc tùy chọn tốt hơn nên sử dụng Setter injection vì chúng ta có thể thực hiện tiêm lại.
Nếu hai bean phụ thuộc vào nhau thì chúng ta không nên sử dụng Constructor injection trong cả hai định nghĩa bean. Thay vào đó, chúng ta phải sử dụng tiêm setter vào bất kỳ hạt đậu nào. (tất nhiên chúng ta có thể sử dụng setter injection n cả hai định nghĩa bean, nhưng phương thức chèn constructor trong cả ném 'BeanCurrentlyInCreationException'
Tham khảo tài liệu Spring tại " https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource "