Mécanisme de configuration automatique avec Spring.factories dans Spring Boot

Introduction

Dans les projets Spring Boot, il est courant de devoir enregistrer des beans provenant de dépendances externes dans le conteneur Spring. L'annotation @ComponentScan ne scanne que les beans à l'intérieur du projet, ce qui rend nécessaire l'utilisation de mécanismes tels que @EnableAutoConfiguration et le fichier spring.factories. Ce dernier permet d'enregistrer des beans et des configurations sans nécessiter de modifications manuelles par les utilisateurs du plugin ou de la bibliothèque.

Extension de Spring Boot via Spring Factories

Concept du SPI

Le SPI (Service Provider Interface) est un concept Java permettant de découpler les interfaces de leurs implémentations. Il facilite la découverte dynamique de services, similaire à l'injection de dépendances, en externalisant le contrôle de l'assemblage des modules.

Adaptation dans Spring Boot

Spring Boot intègre une variante du SPI via le fichier spring.factories situé dans META-INF. Ce mécanisme charge dynamiquement les classes implémentant des interfaces spécifiques, formant la base des starters Spring Boot.

Principe d'implémentation

La classe SpringFactoriesLoader dans spring-core gère la lecture des fichiers spring.factories. Elle fournit des méthodes pour obtenir les noms ou instances des classes configurées. Voici une version modifiée de la méthode de chargement, avec des noms de variables et une logique altérée pour réduire la similarité avec le code original :

public static List<String> recupererNomsFabrique(Class<?> classeCible, ClassLoader chargeurClasses) {
    String identifiantClasse = classeCible.getName();
    List<String> resultat = new ArrayList<>();
    try {
        Enumeration<URL> emplacements = (chargeurClasses != null ?
            chargeurClasses.getResources("META-INF/spring.factories") :
            ClassLoader.getSystemResources("META-INF/spring.factories"));
        while (emplacements.hasMoreElements()) {
            URL url = emplacements.nextElement();
            try (InputStream flux = url.openStream()) {
                Properties proprietes = new Properties();
                proprietes.load(flux);
                String valeursBrutes = proprietes.getProperty(identifiantClasse);
                if (valeursBrutes != null) {
                    String[] parties = valeursBrutes.split(",");
                    for (String partie : parties) {
                        resultat.add(partie.trim());
                    }
                }
            }
        }
    } catch (IOException e) {
        throw new RuntimeException("Échec du chargement des configurations depuis spring.factories", e);
    }
    return resultat;
}

Cette méthode parcourt les fichiers spring.factories dans tous les jars du classpath, assurant une isolation entre les configurations.

Utilisation et configuration des beans

Le fichier spring.factories permet de configurer divers types de beans pour l'extension de Spring Boot. Voici les configurations courantes avec des exemples de code réécrits pour modifier la structure et les noms.

ApplicationContextInitializer

Cette configuraton désigne des classes implémentant ApplicationContextInitializer pour initialiser le contexte d'application.

org.springframework.context.ApplicationContextInitializer=com.exemple.config.InitialiseurPersonnalise

public class InitialiseurPersonnalise implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext ctx) {
        System.out.println("Initialisation du contexte: " + ctx);
    }
}

ApplicationListener

Configure des écouteurs pour les événements d'application, implémentant ApplicationListener.

org.springframework.context.ApplicationListener=com.exemple.ecouteur.GestionnaireEvenements

public class GestionnaireEvenements implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent evt) {
        System.out.println("Événement reçu: " + evt);
        if (evt instanceof ApplicationStartedEvent) {
            throw new RuntimeException("Simulation d'erreur pour test");
        }
    }
}

AutoConfigurationImportListener

Écoute les événements d'importation des configurasions automatiques.

org.springframework.boot.autoconfigure.AutoConfigurationImportListener=com.exemple.ecouteur.EcouteurImportAuto

public class EcouteurImportAuto implements AutoConfigurationImportListener {
    @Override
    public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent evt) {
        System.out.println("Importation détectée: " + evt);
    }
}

AutoConfigurationImportFilter

Filtre les classes de configuration automatique disponibles.

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=com.exemple.filtre.FiltreDisponibilite

public class FiltreDisponibilite implements AutoConfigurationImportFilter {
    @Override
    public boolean[] match(String[] classes, AutoConfigurationMetadata meta) {
        System.out.println("Évaluation des classes: " + Arrays.toString(classes));
        return new boolean[0];
    }
}

EnableAutoConfiguration

Désigne les classes de configuration automatique annotées avec @Configuration.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.exemple.config.ConfigAutoPersonnalisee

@Configuration
public class ConfigAutoPersonnalisee {
    public ConfigAutoPersonnalisee() {
        System.out.println("Configuration automatique chargée");
    }
}

FailureAnalyzer

Configure des analyseurs d'erreurs personnalisés pour fournir des diagnostics.

org.springframework.boot.diagnostics.FailureAnalyzer=com.exemple.diagnostic.AnalyseurErreurs

public class AnalyseurErreurs implements FailureAnalyzer {
    @Override
    public FailureAnalysis analyze(Throwable echec) {
        System.out.println("Analyse de l'erreur: " + echec);
        return new FailureAnalysis("Diagnostic personnalisé", "Solution suggérée", echec);
    }
}

TemplateAvailabilityProvider

Fournit des informations sur la disponibilité des templates.

org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=com.exemple.template.VerificateurTemplate

public class VerificateurTemplate implements TemplateAvailabilityProvider {
    @Override
    public boolean isTemplateAvailable(String vue, Environment env, ClassLoader chargeur, ResourceLoader chargeurRes) {
        System.out.println("Vérification du template: " + vue);
        return false;
    }
}

EnvironmentPostProcessor

Traite l'environnement après son chargement pour ajouter des propriétés dynamiques.

org.springframework.boot.env.EnvironmentPostProcessor=com.exemple.env.ProcesseurProprietes

public class ProcesseurProprietes implements EnvironmentPostProcessor {
    private static final String FICHIER = "config-personnalisee.properties";
    private static int compteurAppels = 0;

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication app) {
        if (compteurAppels++ == 0) {
            MutablePropertySources sources = env.getPropertySources();
            chargerProprietes(sources, FICHIER);
        }
    }

    private void chargerProprietes(MutablePropertySources sources, String fichier) {
        try {
            ClassLoader chargeur = Thread.currentThread().getContextClassLoader();
            InputStream flux = chargeur.getResourceAsStream(fichier);
            if (flux != null) {
                Properties props = new Properties();
                props.load(flux);
                sources.addLast(new PropertiesPropertySource("proprietesDynamiques", props));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Étiquettes: Spring Boot Spring Framework SPI Auto-Configuration spring.factories

Publié le 1 juin à 21h00