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