是否可以保证调用@PostConstruct方法的顺序?已接受的解决方案的说明
我有一个系统,它使用Spring进行依赖注入。我使用基于注释的自动布线。这些豆子是通过组件扫描发现的,即我的上下文XML包含以下内容:
<context:component-scan base-package="org.example"/>
我在下面创建了一个noddy示例来说明我的问题。
有一个它是对象的容器。的开发人员在开发时不知道将包含哪些对象 ;在编译时,Spring实例化的具体对象集是已知的,但是有各种构建配置文件导致不同的s集,并且在这些情况下,代码不得更改。Zoo
Animal
Zoo
Animal
Zoo
Animal
Animal
Zoo
的目的是允许系统的其他部分(此处显示为 )在运行时访问对象集,而无需显式依赖于某些对象。Zoo
ZooPatron
Animal
Animal
实际上,具体类都将由各种Maven工件贡献。我希望能够通过简单地依靠包含这些具体s的各种工件来组装我的项目的发行版,并在编译时正确连接所有内容。Animal
Animal
我试图通过让个人依赖于,以便他们可以在期间调用注册方法来解决此问题(未成功)。这避免了显式依赖于显式列表的 s。Animal
Zoo
Zoo
@PostConstruct
Zoo
Animal
这种方法的问题在于,只有当所有动物
都注册时,希望与它互动的客户才会与之互动。有一组有限的s在编译时是已知的,注册都发生在我生命周期的Spring连接阶段,所以订阅模型应该是不必要的(即我不希望在运行时添加s)。Zoo
Animal
Animal
Zoo
不幸的是,所有的客户都简单地依赖于.这与 s 与 的关系完全相同。因此,s 和 的方法以任意顺序调用。这用下面的示例代码来说明 - 在 上调用时,没有 s 已注册,当它们全部注册时,几毫秒后。Zoo
Zoo
Animal
Zoo
@PostConstruct
Animal
ZooPatron
@PostConstruct
ZooPatron
Animal
所以这里有两种类型的依赖,我在春天很难表达出来。客户只想在所有内容都在其中后使用它。(也许“方舟”会是一个更好的例子...)Zoo
Animal
我的问题基本上是:解决这个问题的最佳方法是什么?
@Component
public class Zoo {
private Set<Animal> animals = new HashSet<Animal>();
public void register(Animal animal) {
animals.add(animal);
}
public Collection<Animal> getAnimals() {
return animals;
}
}
public abstract class Animal {
@Autowired
private Zoo zoo;
@SuppressWarnings("unused")
@PostConstruct
private void init() {
zoo.register(this);
}
@Component
public static class Giraffe extends Animal {
}
@Component
public static class Monkey extends Animal {
}
@Component
public static class Lion extends Animal {
}
@Component
public static class Tiger extends Animal {
}
}
public class ZooPatron {
public ZooPatron(Zoo zoo) {
System.out.println("There are " + zoo.getAnimals().size()
+ " different animals.");
}
}
@Component
public class Test {
@Autowired
private Zoo zoo;
@SuppressWarnings("unused")
@PostConstruct
private void init() {
new Thread(new Runnable() {
private static final int ITERATIONS = 10;
private static final int DELAY = 5;
@Override
public void run() {
for (int i = 0; i<ITERATIONS; i++) {
new ZooPatron(zoo);
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {
// nop
}
}
}
}).start();
}
}
public class Main {
public static void main(String... args) {
new ClassPathXmlApplicationContext("/context.xml");
}
}
输出:
There are 0 different animals.
There are 3 different animals.
There are 4 different animals.
There are 4 different animals.
... etc
已接受的解决方案的说明
基本上答案是:不,你不能保证呼叫的顺序,而不去“外面”Spring或改变它的行为。@PostConstruct
这里真正的问题不是我想对调用进行排序,那只是依赖关系表达不正确的症状。@PostConstruct
如果的消费者依赖他,反过来又依赖他,那么一切都可以正常工作。我的错误是我不想依赖于子类的显式列表,因此引入了这种注册方法。正如答案中指出的那样,将自注册机制与依赖注入混合在没有不必要的复杂性的情况下永远不会起作用。Zoo
Zoo
Animal
Zoo
Animal
答案是声明它依赖于s的集合,然后允许Spring通过自动布线填充集合。Zoo
Animal
因此,没有集合成员的硬列表,它们是由Spring发现的,但是依赖关系被正确表达,因此方法以我想要的顺序发生。@PostConstruct
感谢您的精彩回答。