Spring:循环依赖,@PostConstruct 和 @DependsOn 强加的顺序

Vic*_*kin 4 java spring

我希望 Spring@DependsOn在调用@PostConstruct方法时考虑到这一点,但似乎在存在循环(自动连接)依赖项时情况并非如此。

考虑两个 bean(下面的代码),BeanB @DependsOn BeanA. 当现场BeanA#b有它@Autowired注释掉,后期构造方法的调用预期顺序:先A,然后B.但随着@Autowired在甲效果,我有B的post第一调用,那么A的post

我知道这是一个糟糕的设计(实际上,它是非常大的@Autowired...代码库的最小演示),但我期待 Spring 完成@Autowired字段的注入,然后开始调用生命周期回调,尊重@DependsOn,但 Spring 似乎忽略了@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()) +
                '}';
    }
}
Run Code Online (Sandbox Code Playgroud)

Sot*_*lis 5

在关于初始化回调的章节中,Spring 文档指出

[@PostConstruct和其他方法] 允许 bean 在容器设置了 bean 的所有必要属性后执行初始化工作。

使用您注释的代码,会发生以下情况:beanA实例化并保存。容器看到所有必需的属性都已设置,并调用 init ( @PostConstruct) 方法。然后它会去beanB初始化、保存、查看@Autowired、检索保存的beanA、注入它、运行beanB的,@PostConstruct因为它的所有属性都已设置。

在您未注释的代码中,您有一个循环依赖的情况。beanA首先被实例化并被保存。容器注意到它有一个类型为 的注入目标BeanB。要执行此注入,它需要beanBbean。因此它实例化 bean,保存它,看到它依赖于beanA作为注入目标的 a。它检索beanA(之前保存的),注入它,然后beanB设置所有属性并@PostConstruct调用其方法。最后,这个初始化的beanBbean 被注入到 中beanA@PostConstruct然后调用它的方法,因为它的所有属性都已设置。

这第二个案例beanB正在构建时beanA正在构建。这就是Spring如何解决以下问题

class A {
    private B b;
}

class B {
    private A a;
}
Run Code Online (Sandbox Code Playgroud)

必须先创建每个实例,然后才能将其注入另一个。


如果您去掉@DependsOn,您将获得相同的行为(但仅仅是因为类路径扫描的默认顺序,这似乎是按字母顺序排列的)。例如,如果您重命名BeanABeanZbeanB将首先实例化,然后beanZ将被实例化、初始化并返回以注入到 中beanB

@DependsOn 只有当您希望在 bean 初始化之前发生副作用时才真正需要。