我有一个使用Spring进行依赖注入(inject)的系统。我使用基于注释的 Autowiring 。 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
的各种工件来组合项目的发行版,并在编译时正确地 Autowiring 一切。我试图通过使各个
Animal
依赖于Zoo
来解决这个问题(未成功),以便他们可以在Zoo
期间在@PostConstruct
上调用注册方法。这避免了Zoo
显式依赖于Animal
的显式列表。这种方法的问题在于,仅当所有
Zoo
已注册时,Animal
的客户才希望与其进行交互。在编译时已知有一组有限的Animal
,注册全部发生在我生命周期的Spring接线阶段,因此订阅模型应该是不必要的(即,我不希望将Animal
添加到Zoo
在运行时)。不幸的是,
Zoo
的所有客户仅依赖Zoo
。这与Animal
与Zoo
的关系完全相同。因此,@PostConstruct
和Animal
的ZooPatron
方法以任意顺序调用。下面的示例代码对此进行了说明-在@PostConstruct
上调用ZooPatron
时,尚未注册Animal
,所有这些都在几毫秒后注册。因此,这里有两种类型的依赖关系,我正在努力在Spring中表达。一旦所有
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
,则一切正常。我的错误是我不希望Zoo
依赖于Animal
子类的显式列表,因此引入了这种注册方法。正如答案中指出的那样,如果没有不必要的复杂性,将自注册机制与依赖项注入(inject)混合在一起将永远无法工作。答案是声明
Zoo
依赖于Animal
的集合,然后允许Spring通过 Autowiring 来填充该集合。因此,没有收集成员的硬列表,它们是由Spring发现的,但是正确表达了依赖关系,因此
@PostConstruct
方法按我想要的顺序发生。感谢您的出色回答。
最佳答案
您可能反而将动物组@Inject
编入了Zoo。
@Component
public class Zoo {
@Inject
private Set<Animal> animals = new HashSet<Animal>();
// ...
}
然后,只有在注入(inject)所有动物后,才应调用Zoo的
@PostConstruct
。唯一的难题是系统中至少必须有一个动物,但这听起来不应该是一个问题。关于java - 是否可以保证@PostConstruct方法的调用顺序?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7066683/