我有一个使用Spring进行依赖注入的系统。我使用基于注释的自动装配。通过组件扫描发现bean,即我的上下文XML包含:
<context:component-scan base-package="org.example"/>
我在下面创建了一个例子来说明我的问题。
Zoo
是Animal
个对象的容器。 Zoo
的开发人员在开发Animal
时不知道将包含哪些Zoo
个对象; Spring实例化的具体Animal
对象集在编译时是已知的,但是有各种构建配置文件导致各种Animal
集合,Zoo
的代码不得更改在这种情况下。
Zoo
的目的是允许系统的其他部分(此处显示为ZooPatron
)在运行时访问Animal
个对象集,而无需明确依赖某些对象Animal
秒。
实际上,具体的Animal
类将全部由各种Maven工件提供。我希望能够通过简单地依赖包含这些具体Animal
的各种工件来组装我的项目的分布,并在编译时使所有内容自动装配。
我试图通过让Animal
个人依赖Zoo
来解决此问题(失败),以便他们可以在Zoo
期间调用注册方法{ {1}}。这样可以避免@PostConstruct
明确依赖于Zoo
s的明确列表。
此方法存在的问题是Animal
的客户只有在所有Zoo
已注册时才希望与进行互动。有一组有限的Animal
s在编译时是已知的,并且注册都发生在我的生命周期的Spring连接阶段,所以订阅模型应该是不必要的(即我不想添加Animal
{}在运行时{。}}。
不幸的是,Animal
的所有客户都只依赖Zoo
。这与Zoo
与Zoo
的关系完全相同。因此,Animal
和Zoo
的{{1}}方法以任意顺序调用。下面的示例代码对此进行了说明 - 在@PostConstruct
上调用Animal
时,没有注册ZooPatron
,所有注册时都是几毫秒。
所以这里有两种类型的依赖,我在Spring中努力表达。 @PostConstruct
的客户只想在其中包含所有ZooPatron
后才使用它。 (也许“方舟”会是一个更好的例子......)
我的问题基本上是:解决这个问题的最佳方法是什么?
Animal
输出:
Zoo
基本上答案是:不,你不能保证Animal
调用的顺序,而不是在“外部”Spring或修改它的行为。
这里真正的问题是 not ,我想对@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
依赖于@PostConstruct
,则一切正常。我的错误是我不希望Zoo
依赖于明确的Zoo
子类列表,因此引入了这种注册方法。正如答案中所指出的,将自注册机制与依赖注入相结合将永远不会有不必要的复杂性。
答案是声明Animal
依赖于Zoo
的集合,然后允许Spring通过自动连接填充集合。
因此,没有集合成员的硬列表,它们是由Spring发现的,但依赖关系是正确表达的,因此Animal
方法按照我想要的顺序发生。
感谢您的出色答案。
答案 0 :(得分:4)
您可能会将动物集@Inject
编入动物园。
@Component
public class Zoo {
@Inject
private Set<Animal> animals = new HashSet<Animal>();
// ...
}
然后只有在注射所有动物后才能调用Zoo @PostConstruct
。唯一的问题是系统中必须至少有一个Animal,但听起来不应该是一个问题。
答案 1 :(得分:2)
我认为没有办法确保@PostConstruct顺序而不引入依赖关系。
我认为你在寻找混合注射或自我注册的麻烦。在某种程度上,@ PostConstruct调用顺序无关紧要 - 如果确实如此,它可能不是正确的工具。
您的例子有几个想法
我认为没有“正确”的答案,这完全取决于您的使用案例。
答案 2 :(得分:2)
重构您的问题,使其不依赖于调用顺序。
答案 3 :(得分:1)
最好的方法,IMO,是避免在构造对象图时做太多工作(就像在Java中一样,你避免在构造函数中做太多的工作),并避免在你'时从依赖中调用方法'我不确定它们是否已完全初始化。
如果您只是从Test#init()
方法中删除@PostConstruct注释,并且只是从main方法调用它,则在创建上下文后,您将不再遇到此问题。
答案 4 :(得分:0)
在我看来,在您的情况下,Zoo对象与您的所有动物类型之间存在依赖关系。如果您设计Zoo对象以反映此依赖关系,则问题将得到解决。 例如,您可以这样做:
<bean id="zoo" class="Zoo">
<property name="animals">
<list>
<ref bean="Monkey" />
<ref bean="Tiger" />
<ref bean="Lion" />
</list>
</property>
</bean>
而不是使用寄存器方法。