Spring:循环依赖,@ PostConstruct和@DependsOn施加的顺序 [英] Spring: circular dependencies, @PostConstruct and order imposed by @DependsOn

查看:431
本文介绍了Spring:循环依赖,@ PostConstruct和@DependsOn施加的顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望Spring在调用 @PostConstruct 方法时考虑到 @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.

考虑两个bean(下面的代码), BeanB @DependsOn BeanA .当将 BeanA#b 字段的 @Autowired 注释掉时,将按预期顺序调用构造后方法:首先是A,然后是B.但是使用 @Autowired 对A有效,我先调用B的 post ,然后然后 A的 post .

Consider two beans (code below), 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.

我知道这是一个不好的设计(实际上,这是非常大的 @Autowired ...代码库的最小演示),但是我期望Spring完成 @Autowired的注入字段和 then 开始调用生命周期回调,以尊重 @DependsOn ,但是当循环出现时,Spring似乎忽略了 @DependsOn 顺序.部门.

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.

春季版本为4.1.5.

Spring version is 4.1.5.

那么,这是我的误解还是未记录的行为?还是可以将其视为 Spring错误(或者也许是功能要求)?

So, is this my misunderstanding or undocumented behavior or can it be considered a Spring bug (or, perhaps, feature request)?

@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

[ @PostConstruct 和其他方法]允许bean执行在Bean上所有必需的属性都具有后,进行初始化工作由容器设置.

[@PostConstruct and other methods] allows a bean to perform initialization work after all necessary properties on the bean have been set by the container.

使用您的注释代码,将发生以下情况: beanA 被实例化并保存.容器看到已经设置了所有必需的属性,并调用了init( @PostConstruct )方法.然后转到 beanB ,对其进行初始化,保存,看到 @Autowired ,检索保存的 beanA ,将其注入,运行beanB @PostConstruct ,因为其所有属性均已设置.

With your commented code, the following happens: beanA is instantiated and saved. The container sees that all necessary properties have been set and it invokes the init (@PostConstruct) method. 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.

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

In your uncommented code, you have a case of circular dependencies. beanA gets instantiated first and is saved. The container notices it has an injection target of type BeanB. To perform this injection, it needs the beanB bean. It therefore instantiates the bean, saves it, sees that it has a dependency on a beanA as an injection target. It retrieves the beanA (which was saved earlier), injects it, then beanB's properties are all set and its @PostConstruct method is invoked. Finally, this initialized beanB bean is injected into beanA, whose @PostConstruct method is then invoked since all its properties have been set.

此秒中正在构造 beanA 的情况下正在构造 beanB 的情况.这就是Spring解决以下问题的方式

This second has case beanB being constructed while beanA is being constructed. This is how Spring solves the following

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.

如果摆脱了 @DependsOn ,您将得到相同的行为(但仅仅是由于类路径扫描的默认顺序,这似乎是字母顺序).例如,如果将 BeanA 重命名为 BeanZ ,则将首先实例化 beanB ,然后将实例化 beanZ ,初始化并返回以注入 beanB .

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). 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.

@DependsOn 实际上仅在您具有在初始化bean之前希望发生的副作用的情况下才需要.

@DependsOn is really only necessary if you have side effects that you'd like to happen before a bean is initialized.

这篇关于Spring:循环依赖,@ PostConstruct和@DependsOn施加的顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆