Is it possible to guarantee the order in which @PostConstruct methods are invoked?

wool.in.silver picture wool.in.silver · Aug 15, 2011 · Viewed 17.2k times · Source

I have a system which is using Spring for dependency injection. I use annotation-based autowiring. The beans are discovered by component scanning, i.e. my context XML contains this:

<context:component-scan base-package="org.example"/>

I have created a noddy example below to illustrate my problem.

There is a Zoo which is a container for Animal objects. The developer of Zoo does not know which Animal objects will be contained whilst he is developing Zoo; the set of concrete Animal objects instantiated by Spring is known at compile-time, but there are various build profiles resulting in various sets of Animals, and the code for Zoo must not change under these circumstances.

The purpose of Zoo is to allow other parts of the system (illustrated here as ZooPatron) to access the set of Animal objects at runtime, without needing to depend explicitly on certain Animals.

Actually, the concrete Animal classes will all be contributed by various Maven artifacts. I want to be able to assemble a distribution of my project by simply depending on the various artifacts containing these concrete Animals, and have everything autowire correctly at compile-time.

I have attempted to solve this problem (unsuccessfully) by having the individual Animals depend upon the Zoo, in order that they can call a registration method on the Zoo during @PostConstruct. This avoids the Zoo depending explicitly on an explicit list of Animals.

The problem with this approach is that the customers of Zoo wish to interact with it only when all the Animals have registered. There is a finite set of Animals which is known at compile-time, and the registration all happens during the Spring wiring phase of my lifecycle, so a subscription model should be unneccesary (i.e. I don't wish to add Animals to the Zoo at runtime).

Unfortunately, all the customers of Zoo simply depend upon Zoo. This is exactly the same relationship which the Animals have with Zoo. Therefore, the @PostConstruct methods of the Animals and ZooPatron are called in an arbitrary sequence. This is illustrated with the example code below - at the time @PostConstruct is invoked on ZooPatron, no Animals have registered, it is some milliseconds later when they all register.

So there are two types of dependency here, which I am struggling to express in Spring. The customers of Zoo only want to use it once all the Animals are in it. (perhaps "Ark" would have been a better example...)

My question is basically: what is the best way to solve this problem?

@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");
    }

}

Output:

There are 0 different animals.
There are 3 different animals.
There are 4 different animals.
There are 4 different animals.
... etc

Explanation of accepted solution

Basically the answer is: no, you cannot guarantee the order of @PostConstruct calls without either going "outside" Spring or modifying its behaviour.

The real problem here was not that I wanted to sequence the @PostConstruct invocations, that was merely a symptom of the dependencies being expressed incorrectly.

If the consumers of Zoo depend upon him, and Zoo in turn depends upon Animals, everything works correctly. My mistake was that I didn't want Zoo to depend upon an explicit list of Animal subclasses, and therefore introduced this registration method. As pointed out in the answers, mixing a self-registration mechanism with dependency injection will never work without unnecessary complexity.

The answer is to declare that Zoo is dependent upon a collection of Animals, then allow Spring to populate the collection through auto-wiring.

Thus, there is no hard list of collection members, they are discovered by Spring, but the dependencies are correctly expressed and therefore the @PostConstruct methods happen in the sequence I want.

Thanks for the excellent answers.

Answer

Brian Kent picture Brian Kent · Aug 15, 2011

You might instead have the Set of Animals @Injected into the Zoo.

@Component
public class Zoo {

    @Inject
    private Set<Animal> animals = new HashSet<Animal>();

    // ...
}

Then Zoo's @PostConstruct should only be called once all the Animals are injected. The only gotcha is that there must be at least one Animal in the system, but it doesn't sound like that should be an issue.