[英]Spring: circular dependencies, @PostConstruct and order imposed by @DependsOn
I was expecting Spring to take @DependsOn
into account when calling @PostConstruct
methods, but seems like it's not the case in presence of circular (auto-wired) dependencies.我希望 Spring 在调用
@PostConstruct
方法时考虑@DependsOn
,但似乎在存在循环(自动连接)依赖项的情况下并非如此。
Consider two beans (code below), BeanB @DependsOn BeanA
.考虑两个 bean(下面的代码),
BeanB @DependsOn BeanA
。 When field BeanA#b
has it's @Autowired
commented out, post-construct methods are called in expected order: first A, then B. But with @Autowired
in effect for A, I have B's post
called first, then A's post
.当字段
BeanA#b
将其@Autowired
注释掉时,按预期顺序调用后构造方法:首先是 A,然后是 B。但是由于@Autowired
对 A 有效,我先调用 B 的post
,然后调用 A 的post
。
I understand this is a bad design (actually, it's minimal demo of very big @Autowired
... code-base), but I was expecting Spring to finish injection of @Autowired
fields and then starting to call lifecycle callbacks, honoring @DependsOn
, but Spring seems to ignore @DependsOn
order when there are circular deps.我知道这是一个糟糕的设计(实际上,它是非常大的
@Autowired
... 代码库的最小演示),但我期待 Spring 完成@Autowired
字段的注入,然后开始调用生命周期回调,尊重@DependsOn
,但是当有循环 deps 时,Spring 似乎忽略了@DependsOn
顺序。
Spring version is 4.1.5. Spring 版本是 4.1.5。
So, is this my misunderstanding or undocumented behavior or can it be considered a Spring bug (or, perhaps, feature request)?那么,这是我的误解或未记录的行为,还是可以将其视为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()) +
'}';
}
}
In the chapter about Initialization callbacks , the Spring documentation states在关于初始化回调的章节中,Spring 文档指出
[
@PostConstruct
and other methods] allows a bean to perform initialization work after all necessary properties on the bean have been set by the container.[
@PostConstruct
和其他方法] 允许在容器设置 bean 的所有必要属性后,bean 执行初始化工作。
With your commented code, the following happens: beanA
is instantiated and saved.使用您的注释代码,会发生以下情况:
beanA
被实例化并保存。 The container sees that all necessary properties have been set and it invokes the init ( @PostConstruct
) method.容器看到所有必需的属性都已设置并调用 init (
@PostConstruct
) 方法。 Then it goes to beanB
which it initializes, saves, sees an @Autowired
, retrieves the saved beanA
, injects it, the runs beanB
's @PostConstruct
since all its properties have been set.然后它转到
beanB
,它初始化、保存、查看@Autowired
、检索保存的beanA
、注入它,运行beanB
的@PostConstruct
因为它的所有属性都已设置。
In your uncommented code, you have a case of circular dependencies.在您未注释的代码中,您有一个循环依赖的情况。
beanA
gets instantiated first and is saved. beanA
被实例化并被保存。 The container notices it has an injection target of type BeanB
.容器注意到它有一个
BeanB
类型的注入目标。 To perform this injection, it needs the beanB
bean.要执行此注入,它需要
beanB
bean。 It therefore instantiates the bean, saves it, sees that it has a dependency on a beanA
as an injection target.因此它实例化 bean,保存它,看到它依赖
beanA
作为注入目标。 It retrieves the beanA
(which was saved earlier), injects it, then beanB
's properties are all set and its @PostConstruct
method is invoked.它检索
beanA
(之前保存的),注入它,然后beanB
的属性全部设置并调用它的@PostConstruct
方法。 Finally, this initialized beanB
bean is injected into beanA
, whose @PostConstruct
method is then invoked since all its properties have been set.最后,这个初始化的
beanB
bean 被注入到beanA
,然后调用它的@PostConstruct
方法,因为它的所有属性都已设置。
This second has case beanB
being constructed while beanA
is being constructed.第二个案例
beanB
正在构建,而beanA
正在构建。 This is how Spring solves the following这就是Spring如何解决以下问题
class A {
private B b;
}
class B {
private A a;
}
An instance of each has to be created before either can be injected into the other.必须先创建每个实例,然后才能将其注入另一个。
If you get rid of the @DependsOn
, you'll get the same behavior (but just because of the default ordering of classpath scanning, which seems to be alphabetical).如果你去掉
@DependsOn
,你会得到相同的行为(但仅仅是因为类路径扫描的默认顺序,这似乎是按字母顺序排列的)。 If you renamed BeanA
to BeanZ
, for example, the beanB
will be instantiated first, then beanZ
would get instantiated, initialized, and returned to be injected into beanB
.如果你改名
BeanA
到BeanZ
,例如beanB
将首先实例化,然后beanZ
会得到实例化,初始化,并返回到注入beanB
。
@DependsOn
is really only necessary if you have side effects that you'd like to happen before a bean is initialized. @DependsOn
只有在您希望在 bean 初始化之前发生的副作用时才真正需要。
Inspired by Sotirios' answer, and with some investigation:受到 Sotirios 回答的启发,并进行了一些调查:
class A {
private B b;
}
class B {
private A a;
}
What spring will do is instantiate an a
and b
, spring 会做的是实例化
a
和b
,
Then it does "setup phase" for a
.然后它为
a
. It sets ab=b;
它设置
ab=b;
Then it will call @PostConstruct
method on a
.然后,它会调用
@PostConstruct
的方法a
。
Then it does "setup" for b.然后它为 b 做“设置”。 It sets
ba=a;
它设置
ba=a;
and then calls @PostConstruct
method on b.然后在 b 上调用
@PostConstruct
方法。
So if you look carefully, at a
's @PostConstruct
time, b
hasn't been..fully set up yet.所以如果你仔细看,在
a
的@PostConstruct
时间, b
还没有......完全设置好。 Like... @AutoWired
's haven't even been assigned yet.就像...
@AutoWired
甚至还没有被分配。
ba
is null during a
's @PostConstruct
. ba
在a
的@PostConstruct
期间为空。
Apparently this is "the spring way"?显然这是“春天的方式”?
So if a
's @PostConstruct
calls some method on b b.tell_me_about_a
, b's circular a
will not be assigned yet, so the end result (if tell_me_about_a calls a method of a
) is a callstack that's like所以,如果
a
的@PostConstruct
调用b上的一些方法b.tell_me_about_a
,B的圆a
不会被尚未分配,所以最终的结果(如果tell_me_about_a调用的方法a
)是调用栈这就像
NullPointerException # calling some A method
someLineOfB
somePostConstructMethodOfA
However, during b
's @PostConstruct
ba
will have the instance already fully initiated, and is not lacking its @AutoWired
's, so bab
will not be a NullPointerException.但是,在
b
的@PostConstruct
期间ba
将有实例已经完全启动,并且不缺少它的@AutoWired
,因此bab
不会是 NullPointerException。 Confusing...令人困惑...
@DependsOn
can change some ordering. @DependsOn
可以更改一些顺序。 But it seems that it's in the opposite order to what you'd expect .但它似乎与您期望的顺序相反。 I think you are right when it is circular you can't depend on @DependsOn [?] Because it works as expected (runs PostConstruct's in the expected order if there isn't. To me this feels like a bug.
我认为当它是循环的时,你是对的,你不能依赖 @DependsOn [?] 因为它按预期工作(如果没有,则按预期顺序运行 PostConstruct。对我来说,这感觉像是一个错误。
You can see this behavior in action by adding spring logging , then you'll see messages like DEBUG main support.DefaultListableBeanFactory:247 - Returning eagerly cached instance of singleton bean 'beanA' that is not fully initialized yet - a consequence of a circular reference
demo您可以通过添加 spring logging来查看此行为,然后您将看到诸如
DEBUG main support.DefaultListableBeanFactory:247 - Returning eagerly cached instance of singleton bean 'beanA' that is not fully initialized yet - a consequence of a circular reference
类的消息DEBUG main support.DefaultListableBeanFactory:247 - Returning eagerly cached instance of singleton bean 'beanA' that is not fully initialized yet - a consequence of a circular reference
演示
Ways you can fix it:您可以修复它的方法:
Be careful using methods of injected dependencies during PostConstruct.在 PostConstruct 期间小心使用注入依赖项的方法。 Or call none.
或者不叫。
Using spring-wired constructor parameters instead of @AutoWired/@Inject
at all.完全使用弹簧接线构造函数参数而不是
@AutoWired/@Inject
。 Spring disallows circular in that case.在这种情况下,Spring 不允许使用循环。 Which may be what you want anyway...
无论如何,这可能是你想要的......
Use @DependsOn in reverse order?以相反的顺序使用@DependsOn? what?...
什么?...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.