我有一个使用 Spring 进行依赖注入的系统。我使用基于注释的自动装配。bean 是通过组件扫描发现的,即我的上下文 XML 包含以下内容:
<context:component-scan base-package="org.example"/>
我在下面创建了一个点头示例来说明我的问题。
有一个Zoo
which 是Animal
对象的容器。的开发者在开发过程Zoo
中不知道Animal
将包含哪些对象Zoo
;Spring 实例化的具体Animal
对象集在编译时是已知的,但是有各种构建配置文件会导致各种Animal
s 集,并且Zoo
在这些情况下不能更改代码。
的目的Zoo
是允许系统的其他部分(此处显示为ZooPatron
)在运行时访问Animal
对象集,而无需显式依赖某些Animal
s。
实际上,具体的Animal
类都将由各种 Maven 工件贡献。我希望能够通过简单地依赖包含这些具体Animal
的各种工件来组装我的项目的分发,并在编译时正确地自动装配所有内容。
我试图通过让个人Animal
s 依赖 ,来解决这个问题(不成功) Zoo
,以便他们可以在Zoo
during上调用注册方法@PostConstruct
。这避免了Zoo
显式依赖Animal
s 的显式列表。
这种方法的问题是,只有在所有s 都注册Zoo
后,客户才希望与它进行交互。在编译时已知有限的s 集,并且注册都发生在我生命周期的 Spring 连接阶段,因此订阅模型应该是不必要的(即我不希望在运行时添加s )。Animal
Animal
Animal
Zoo
不幸的是,所有的客户Zoo
都只依赖Zoo
. Animal
这与s 与的关系完全相同Zoo
。因此,s 和的@PostConstruct
方法以任意顺序调用。下面的示例代码说明了这一点 - 在调用 on 时,没有s 已注册,它们都注册时是几毫秒后。Animal
ZooPatron
@PostConstruct
ZooPatron
Animal
所以这里有两种类型的依赖,我在 Spring 中很难表达。一旦所有的s都在其中,客户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
接受解决方案的说明
@PostConstruct
基本上答案是:不,如果不去“外部”Spring 或修改它的行为,就不能保证调用的顺序。
这里真正的问题不是我想对@PostConstruct
调用进行排序,这只是依赖关系表达不正确的症状。
如果 的消费者Zoo
依赖于他,Zoo
反过来又依赖于Animal
s,那么一切正常。我的错误是我不想Zoo
依赖明确的Animal
子类列表,因此引入了这种注册方法。正如答案中所指出的,如果没有不必要的复杂性,将自注册机制与依赖注入混合将永远不会起作用。
答案是声明它Zoo
依赖于 s 的集合,Animal
然后允许 Spring 通过自动装配来填充集合。
因此,没有集合成员的硬性列表,它们是由 Spring 发现的,但是依赖关系被正确表达,因此@PostConstruct
方法按我想要的顺序发生。
感谢您的出色回答。