Contrôler l'ordre d'initialisation des beans Spring avec l'annotation @DependsOn

Dans le framework Spring, l'ordre dans lequel les beans sont chargés par le conteneur n'est pas déterministe par défaut. Cependant, Spring garatnit que si un bean A dépend directement d'un bean B (via @Autowired, par exemple), B sera initialisé avant A. Mais comment forcer l'initialisation préalable de B lorsque A ne dépend pas directement de lui ?

Scénarios nécessitant un contrôle d'ordre

Plusieurs situations exigent qu'un bean B soit initialisé avant un bean A, même sans lien de dépendance directe. Par exemple :

  • B met à jour un cache global (peut-être via un singleton non enregistré dans Spring), et A doit utiliser ce cache.
  • A est un émetteur d'événements, et B est un écouteur ; pour éviter de manquer des événements, B doit être prêt avant l'émission.

Dans ces cas, l'annotation @DependsOn appliquée à A permet d'indiquer au conteneur Spring que B doit être initialisé en premier.

Exemple pratique avec un système d'événements

Illustrons cela avec un exemple simplifié où un émetteur d'événements et un abonné interagissent via un hub centralisé.

EventHub.java

Cette classe gère une liste d'abonnés et permet d'émettre des événements.

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class EventHub {
    private static final EventHub INSTANCE = new EventHub();
    private final List<Consumer<String>> subscribers = new ArrayList<>();

    public static EventHub getInstance() {
        return INSTANCE;
    }

    public void addSubscriber(Consumer<String> callback) {
        subscribers.add(callback);
    }

    public void emitEvent(String message) {
        subscribers.forEach(sub -> sub.accept(message));
    }
}

EventEmitter.java

Ce bean émet un événement lors de son initialisation.

public class EventEmitter {

    public void initialize() {
        System.out.println("Initialisation de l'émetteur d'événements");
        EventHub.getInstance().emitEvent("Événement émis depuis l'émetteur");
    }
}

EventSubscriber.java

Ce bean s'abonne aux événements lors de son initiailsation.

public class EventSubscriber {

    public void initialize() {
        EventHub.getInstance()
                .addSubscriber(msg ->
                        System.out.println("Message reçu par l'abonné : " + msg));
    }
}

AppConfig.java

La configuration Spring qui utilise @DependsOn pour contrôler l'ordre d'initialisation.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

@Configuration
@ComponentScan("com.example")
public class AppConfig {

    @Bean(initMethod = "initialize")
    @DependsOn("eventSubscriber")
    public EventEmitter eventEmitter() {
        return new EventEmitter();
    }

    @Bean(name = "eventSubscriber", initMethod = "initialize")
    public EventSubscriber eventSubscriber() {
        return new EventSubscriber();
    }

    public static void main(String[] args) {
        new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

L'exécution de cette configuraton produit la sortie suivante :

Initialisation de l'abonné d'événements
Initialisation de l'émetteur d'événements
Message reçu par l'abonné : Événement émis depuis l'émetteur

Analyse et alternatives

Si l'annotation @DependsOn est omise, l'ordre d'initialisation devient aléatoire, et il est possible que l'abonné ne reçoive pas l'événement émis lors de l'initialisation de l'émetteur.

Une alternative est d'utiliser @Lazy sur l'abonné pour retarder son initialisation jusqu'à ce qu'il soit nécessaire. Cependant, même avec @Lazy, si @DependsOn est présent, le bean référencé sera initialisé au démarrage.

L'annotation @DependsOn accepte un tableau de noms de beans, permettant de spécifier plusieurs dépendances :

@DependsOn({"beanB", "beanC", "beanD"})
public class BeanA {
    // ...
}

Étiquettes: spring-framework Java depends-on bean-lifecycle event-driven-programming

Publié le 6 juillet à 00h49