我希望 Spring 在调用 @DependsOn
方法时考虑 @PostConstruct
,但似乎在存在循环(自动连接)依赖项时情况并非如此。
考虑两个 bean(下面的代码), BeanB @DependsOn BeanA
。当字段 BeanA#b
将其 @Autowired
注释掉时,后构造方法按预期顺序调用:首先是 A,然后是 B。但是由于 @Autowired
对 A 有效,我先调用了 B 的 post
,然后是 A 的 post
。
我知道这是一个糟糕的设计(实际上,它是非常大的 @Autowired
... 代码库的最小演示),但我期待 Spring 完成 @Autowired
字段的注入(inject),然后开始调用生命周期回调,尊重 @DependsOn
,但 Spring 似乎当有循环 deps 时忽略 @DependsOn
顺序。
Spring 版本是 4.1.5。
那么,这是 我的误解 或 未记录的行为 还是可以将 视为 Spring 错误 (或者,可能是功能请求)?
@Component
class BeanA {
// @Autowired
private BeanB b;
void f() {
System.out.println(this);
}
@PostConstruct
void post() {
System.out.println("A done");
}
@Override
public String toString() {
return "Bean{" +
"b=" + (b == null ? null : b.getClass()) +
'}';
}
}
// ---------------------
@Component
@DependsOn("beanA")
class BeanB {
@Autowired
private BeanA a;
void f() {
System.out.println(this);
}
@PostConstruct
void post() {
System.out.println("B done");
}
@Override
public String toString() {
return "BeanB{" +
"a=" + (a == null ? null : a.getClass()) +
'}';
}
}
最佳答案
在关于 Initialization callbacks 的章节中,Spring 文档指出
使用您的注释代码,会发生以下情况: beanA
被实例化并保存。容器看到所有必要的属性都已设置,并调用 init ( @PostConstruct
) 方法。然后它转到 beanB
,它初始化,保存,查看 @Autowired
,检索保存的 beanA
,注入(inject)它,运行 beanB
的 @PostConstruct
,因为它的所有属性都已设置。
在您未注释的代码中,您有一个循环依赖的情况。 beanA
首先被实例化并被保存。容器注意到它有一个 BeanB
类型的注入(inject)目标。要执行此注入(inject),它需要 beanB
bean。因此它实例化 bean,保存它,看到它依赖于 beanA
作为注入(inject)目标。它检索 beanA
(之前保存的),注入(inject)它,然后 beanB
的属性全部设置并调用其 @PostConstruct
方法。最后,这个初始化的 beanB
bean 被注入(inject)到 beanA
中,然后调用其 @PostConstruct
方法,因为它的所有属性都已设置。
第二个案例 beanB
正在构建,而 beanA
正在构建。这就是Spring如何解决以下问题
class A {
private B b;
}
class B {
private A a;
}
必须先创建每个实例,然后才能将其注入(inject)另一个。
如果你去掉
@DependsOn
,你会得到相同的行为(但仅仅是因为类路径扫描的默认顺序,这似乎是按字母顺序排列的)。例如,如果您将 BeanA
重命名为 BeanZ
,则 beanB
将首先被实例化,然后 beanZ
将被实例化、初始化并返回以注入(inject)到 beanB
中。@DependsOn
只有在您希望在 bean 初始化之前发生的副作用时才真正需要。关于java - Spring:循环依赖,@PostConstruct 和@DependsOn 强加的顺序,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29260676/