[Microservice] Detaillierte Erläuterung der Verwendung der Spring-Control-Bean-Ladereihenfolge

Inhaltsverzeichnis

I. Einleitung

2. Verwenden Sie die Annotation @order, um die Reihenfolge zu steuern

2.1 Beispiele für die Verwendung von @order-Annotationen

2.2 Problem mit der Ungültigmachung der Bestellanmerkungssequenz

2.2.1 Lösungen für das @order-Ungültigmachungsproblem

2.3 Implementieren Sie die Ordered-Schnittstelle

3. Verwenden Sie die @dependon-Annotation, um die Sequenz zu steuern

4. AutoConfiguration-Anmerkungen steuern die Ladereihenfolge der Beans

4.1 @AutoConfigureBefore-Betriebsdemonstration

4.2 Demonstration des @AutoConfigureOrder-Vorgangs

4.3 Interpretation und Analyse des Quellcodes

5. Passen Sie ApplicationContextInitializer an

5.1 Einführung in ApplicationContextInitializer

5.2 Verwendung von ApplicationContextInitializer

5.3 ApplicationContextInitializer steuert die Ladesequenz

6. Nutzungsszenarien

6.1 Bean-Abhängigkeiten auflösen

6.2 Legen Sie die höchste Priorität für bestimmte Konfigurationsklassen fest

6.3 Abhängigkeitsübertragung

7. Schreiben Sie am Ende des Artikels


I. Einleitung

Während der Entwicklung mit dem Spring Framework können folgende Situationen auftreten:

  • Eine Bean ist von einer anderen Bean abhängig, das heißt, die Erstellung von Bean-B muss von Bean-A abhängen.
  • Eine bestimmte Bean hängt von vielen anderen Beans ab. Nachdem Bean-A beispielsweise initialisiert wurde, müssen sich andere Beans auf Bean-A verlassen, um ihr eigenes Geschäft zu initialisieren.
  • ...

Es gibt viele Szenarien wie dieses. Zusammenfassend lässt sich sagen, dass es hier um die Ladereihenfolge von Beans geht. Wie lässt sich das Problem lösen? Nachfolgend sind einige gängige Lösungen aufgeführt.

2. Verwenden Sie die Annotation @order, um die Reihenfolge zu steuern

Die Annotation @order ist eine Annotation unter dem Spring-Core-Paket. Die Funktion von @Order besteht darin, die Priorität der Ausführungsreihenfolge von Beans im Spring IOC-Container zu definieren (die Reihenfolge hier kann auch als die Reihenfolge verstanden werden, in der sie sich befinden). im Behälter aufbewahrt). Konfigurationsabhängigkeiten treten manchmal während des Entwicklungsprozesses auf. Beispielsweise wird @ConditionalOnBean(B.class) verwendet, um Objekt A zu injizieren, was bedeutet, dass A injiziert werden muss, wenn eine Instanz von B.class im Container vorhanden sein muss. Zu diesem Zeitpunkt müssen wir sicherstellen, dass das B-Objekt vor dem A-Objekt injiziert wird.

2.1 Beispiele für die Verwendung von @order-Annotationen

Es gibt zwei Klassen: Demo1 und Demo2. Fügen Sie den Klassen jeweils die Annotation @Order hinzu.

@Component
@Order(1)
public class Demo1 {
    @Bean
    public UserService serviceA(){
        System.out.println("serviceA 执行");
        return new UserService();
    }
}

Die beiden Klassen erstellen jeweils eine UserService-Bean.

@Component
@Order(2)
public class Demo2 {
    @Bean
    public UserService serviceB(){
        System.out.println("serviceB 执行");
        return new UserService();
    }
}

Führen Sie den folgenden Code aus und beobachten Sie den Testeffekt

public class OrderTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanScanConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for(String beanName : beanDefinitionNames){
            System.out.println(beanName);
        }
    }
}

Es ist ersichtlich, dass die Bean der Klasse demo1 mit einer kleineren Bestellnummer zuerst erstellt wird als die Bean der Klasse demo2;

2.2 Problem mit der Ungültigmachung der Bestellanmerkungssequenz

Durch Hinzufügen von @order zur obigen Klasse können Sie die Reihenfolge der von der Klasse erstellten Beans steuern. Ist das wirklich der Fall? Wenn wir zu diesem Zeitpunkt die Bestellnummer von Demo1 erhöhen und die Bestellnummer von Demo2 verringern, sehen Sie, dass ServiceA immer noch zuerst ausgegeben wird. Dies ist das Problem mit der ungültigen Bestellanmerkung. Zu diesem Thema gibt es auch in den offiziellen Dokumenten des Frühjahrs Erläuterungen. Die übersetzte Bedeutung lautet wie folgt:

Sie können die @Order-Annotation auf der Zielklassenebene und in @Bean-Methoden deklarieren, möglicherweise für eine einzelne Bean-Definition (wenn mehrere Definitionen dieselbe Bean-Klasse verwenden). @Order-Werte können sich auf die Priorität von Injektionspunkten auswirken. Beachten Sie jedoch, dass sie keinen Einfluss auf die Startreihenfolge von Singletons haben. Dies ist ein orthogonales Problem, das durch Abhängigkeiten und @DependsOn-Deklarationen bestimmt wird.

2.2.1 Lösungen für das @order-Ungültigmachungsproblem

Lösung 1

Platzieren Sie die beiden Klassen, die die Erstellungsreihenfolge von Beans steuern müssen, in unterschiedlichen Paketpfaden.

Führen Sie den Testcode erneut aus. Wenn die Bestellnummer von Demo2 kleiner ist, können Sie den erwarteten Effekt sehen, wenn die von Demo2 erstellten Beans zuerst ausgegeben werden.

Lösung 2

Benennen Sie die Klasse um, ändern Sie beispielsweise die Klasse von Demo2 in ADemo, sodass die Klasse ADemo vor Demo2 im selben Paketpfad platziert wird.

Führen Sie nach dieser Änderung den Testcode erneut aus, um den Effekt zu beobachten. Zu diesem Zeitpunkt gibt ADemo Demo1 Priorität.

2.3 Implementieren Sie die Ordered-Schnittstelle

Mit der von Spring bereitgestellten geordneten Schnittstelle müssen Sie diese Schnittstelle nur mit einer benutzerdefinierten Klasse implementieren und die darin enthaltene getOrder-Methode neu schreiben. Im Rückgabewert der getOrder-Methode gilt: Je kleiner der Wert, desto höher die Priorität. Die folgenden zwei benutzerdefinierten werden von spspring verwaltet. Klasse, die die Ordered-Schnittstelle implementiert

@Component
public class Listener1 implements Ordered {

    @Bean
    public UserService userService1(){
        System.out.println("userService1 bean");
        return new UserService();
    }

    @Override
    public int getOrder() {
        return 100;
    }
}
@Component
public class Listener2 implements Ordered {

    @Bean
    public UserService userService2(){
        System.out.println("userService2 bean");
        return new UserService();
    }

    @Override
    public int getOrder() {
        return 10;
    }
}

Ähnlich wie bei der geordneten Schnittstelle gibt es PriorityOrdered mit ähnlicher Verwendung. Interessierte Studenten können weiter graben.

3. Verwenden Sie die @dependon-Annotation, um die Sequenz zu steuern

Wenn die Erstellung oder das Laden einer bestimmten Bean-A explizit von einer anderen Bean-B abhängen muss, können Sie die Annotation „dependon“ verwenden. Mit anderen Worten: Bean-B sollte vor Bean-A erstellt werden;

Es gibt zwei Klassen: OrderDemo1 und OrderDemo2

@Component
@DependsOn("orderDemo2")
public class OrderDemo1 {

    public OrderDemo1(){
        System.out.println("OrderDemo1 ...");
    }

}

Die Erstellung von OrderDemo1 hängt von OrderDemo2 ab

@Component
public class OrderDemo2 {

    public OrderDemo2(){
        System.out.println("OrderDemo2 ...");
    }

}

Führen Sie das Programm aus und Sie können sehen, dass OrderDemo2 vor OrderDemo1 erstellt wird

Da diese Methode die Reihenfolge über den Namen der Bean (String) steuert, werden Sie beim Ändern des Klassennamens der Bean wahrscheinlich vergessen, alle Annotationen zu ändern, die sie verwenden, was ein großes Problem darstellt, also diese Methode Im Allgemeinen Dies wird nicht empfohlen, ist aber dennoch eine Möglichkeit, die Reihenfolge der Bean-Erstellung zu steuern.

4. AutoConfiguration-Anmerkungen steuern die Ladereihenfolge der Beans

Spring Boot bestimmt dynamisch die Konfigurationsreihenfolge automatischer Konfigurationsklassen basierend auf der Situation im aktuellen Container. Es stellt uns drei Anmerkungen zur Verfügung: @AutoConfigureBefore, @AutoConfigureAfter und @AutoConfigureOrder:

@AutoConfigureBefore

Wird für eine automatische Konfigurationsklasse verwendet und gibt an, dass die automatische Konfigurationsklasse konfiguriert und geladen werden muss, nachdem die andere angegebene automatische Konfigurationsklasse konfiguriert wurde.

@AutoConfigureAfter

Wird für eine automatische Konfigurationsklasse verwendet und gibt an, dass die automatische Konfigurationsklasse konfiguriert und geladen werden muss, bevor die andere angegebene automatische Konfigurationsklasse konfiguriert wird.

@AutoConfigureOrder

Bestimmen Sie die Prioritätsreihenfolge beim Laden der Konfiguration und geben Sie dabei die absolute Reihenfolge an (je kleiner die Zahl, desto höher die Priorität).

4.1 @AutoConfigureBefore-Betriebsdemonstration

Definieren Sie zwei Konfigurationsklassen DbConfig1 und DbConfig2. Der Code lautet wie folgt

@Configuration
public class DbConfig1 {

    public DbConfig1(){
        System.out.println("DbConfig1 构建方法...");
    }

}

Darunter wird die Annotation AutoConfigureBefore für die DbConfig2-Klasse verwendet, und es wird erwartet, dass DbConfig2 vor DbConfig1 geladen wird.

@Configuration
@AutoConfigureBefore(DbConfig1.class)
public class DbConfig2 {

    public DbConfig2(){
        System.out.println("DbConfig2 构建方法...");
    }

}

Es ist noch nicht fertig. Sie müssen DbConfig1 und DbConfig2 noch in der Datei spring.factories im Ressourcenverzeichnis für die automatische Assemblierung konfigurieren.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.congge.config.DbConfig2,\
com.congge.config.DbConfig1

Führen Sie den obigen Code aus und beobachten Sie die Konsolenausgabe. Sie können sehen, dass DbConfig2 vor DbConfig1 geladen wird.

Wir können die Anmerkung auch in „AutoConfigureAfter“ ändern, um zu sehen, welche Auswirkung dies hat.

@Configuration
@AutoConfigureAfter(DbConfig1.class)
public class DbConfig2 {

    public DbConfig2(){
        System.out.println("DbConfig2 构建方法...");
    }

}

Führen Sie den Code erneut aus und Sie können sehen, dass dieses Mal DbConfig1 zuerst geladen wird.

4.2 Demonstration des @AutoConfigureOrder-Vorgangs

Sie können auch die Annotation „AutoConfigureOrder“ verwenden, um die Ladereihenfolge verschiedener Konfigurationsklassen zu steuern. Je kleiner die Zahl, desto höher die Priorität. Fügen Sie zwei Konfigurationsklassen hinzu und verwenden Sie diese Annotation, um sie jeweils zu markieren.

@Configuration
@AutoConfigureOrder(2)
public class OrderConfig1 {

    public OrderConfig1(){
        System.out.println("OrderConfig1 加载...");
    }
}

Der Wert in OrderConfig2 ist größer. Theoretisch wird OrderConfig1 zuerst geladen.

@Configuration
@AutoConfigureOrder(7)
public class OrderConfig2 {

    public OrderConfig2(){
        System.out.println("OrderConfig2 加载...");
    }
}

Ebenso erfordert die Verwendung von AutoConfigureOrder zur Steuerung der Ladereihenfolge von Konfigurationsklassen auch die Konfiguration für spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.congge.config.DbConfig2,\
com.congge.config.DbConfig1,\
com.congge.config.OrderConfig1,\
com.congge.config.OrderConfig2

Führen Sie den obigen Code aus und sehen Sie den folgenden Effekt, der darauf hinweist, dass OrderConfig1 besser geladen wird als OrderConfig2

4.3 Interpretation und Analyse des Quellcodes

Der kritischste Code befindet sich in der Klasse AutoConfigurationImportSelector. Während des Bean-Ladevorgangs lädt Spring die automatischen Konfigurationsklassen aus spring.factories und sortiert sie nach Bedingungen. Die letzte Codezeile in der Methode selectImports() wird wie folgt sortiert

Nach der sortAutoConfigurations-Methode kommt die endgültige Sortierlogik zur getInPriorityOrder-Methode unten;

Bei dieser Methode erfolgt die Sortierung auf drei Arten:

  • Zuerst alphabetisch sortieren;
  • Dann sortieren Sie nach @AutoConfigureOrder;
  • Sortieren Sie abschließend nach @AutoConfigureBefore und @AutoConfigureAfter;

Anhand der Konfigurationsreihenfolge lässt sich leicht erkennen, dass die endgültige Entscheidung bei den beiden Annotationen @AutoConfigureAfter und @AutoConfigureBefore liegt.

5. Passen Sie ApplicationContextInitializer an

Schüler, die mit dem Lebenszyklus und dem Ladevorgang von Spring Beans vertraut sind, müssen wissen, dass Beans während des Container-Startvorgangs durch Parsen der Konfiguration in der XML-Datei oder durch Scannen des Pfads und Parsen der Beans generiert werden. Diese Beans werden schließlich gespeichert in einem Behälter. Es wird bis zum Frühjahr verwaltet. In diesem Fall können Entwickler manuell in den Parsing-Prozess dieser Bean eingreifen. Über die von Spring bereitgestellten relevanten Erweiterungspunkte kann durch Ändern der Position der Bean im Container die Reihenfolge der Beans gesteuert werden. Schauen wir uns den spezifischen Vorgangsprozess an.

5.1 Einführung in ApplicationContextInitializer

Schauen wir uns zunächst die Einführung auf der offiziellen Website von Spring an:

Die grob übersetzte Bedeutung ist wie folgt

  • Rückrufschnittstelle, die zum Initialisieren von Spring ConfigurableApplicationContext verwendet wird, bevor der Spring-Container aktualisiert wird. (Kurz gesagt wird die Initialisierungsmethode dieser Klasse aufgerufen, bevor der Container aktualisiert wird. Und eine Instanz der ConfigurableApplicationContext-Klasse wird an diese Methode übergeben.)
  • Wird normalerweise in Webanwendungen verwendet, die eine programmgesteuerte Initialisierung des Anwendungskontexts erfordern. Beispielsweise das Registrieren von Attributquellen oder das Aktivieren von Konfigurationsdateien entsprechend der Kontextumgebung;
  • Sortierbar (implementieren Sie die Ordered-Schnittstelle oder fügen Sie die Annotation @Order hinzu);

5.2 Verwendung von ApplicationContextInitializer

Erstellen Sie eine neue Klasse MyAppInitializer und implementieren Sie die ApplicationContextInitializer-Schnittstelle

public class MyAppInitializer implements ApplicationContextInitializer {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("-----MyAppInitializer initialize-----");
    }
}

Fügen Sie es dann in der Startup-Klasse hinzu

@SpringBootApplication
public class BootApp {
    
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(BootApp.class);
        application.addInitializers(new MyAppInitializer());
        application.run(args);
    }
}

Führen Sie das Hauptprogramm oben aus und sehen Sie, wie die Konsole beim Start die folgenden Informationen ausgibt

Aus der obigen Einführung wissen wir, dass die Implementierung der ApplicationContextInitializer-Schnittstelle geladen wird, bevor der Container aktualisiert wird, was dem Spring-Quellcode, also der Methode „refreshContext(context)“ entspricht. Studenten, die den Ladevorgang von Spring Beans verstehen, sollten es wissen Die Hauptfunktion der Refresh-Methode von AbstractApplicationContext besteht darin, Beans im Spring-Container zu registrieren, Beans zu verarbeiten und Funktionen zu senden. Kurz gesagt, bei dieser Methode handelt es sich um eine Reihe von Beans, die im System erstellt und gespeichert werden abgeschlossen sind.

Auf dieser Grundlage wird gemäß dem obigen Effekt nach der Implementierung der ApplicationContextInitializer-Schnittstelle der Ladezeitpunkt ihrer Klassen früher sein. Daher können Sie diese Funktion verwenden, um künstlich in die Erstellungsreihenfolge der Bean einzugreifen, bevor die Bean tatsächlich erstellt wird. Registrieren Sie die angegebene Klasse im Spring-Kontext.

5.3 ApplicationContextInitializer steuert die Ladesequenz

Passen Sie eine Klasse an, um die ApplicationContextInitializer-Schnittstelle zu implementieren

public class MyAppInitializer implements ApplicationContextInitializer {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("-----MyAppInitializer initialize-----");
        applicationContext.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());
    }
}

Passen Sie eine Klasse an, um die Schnittstelle BeanDefinitionRegistryPostProcessor zu implementieren, überschreiben Sie die Methode postProcessBeanDefinitionRegistry und registrieren Sie manuell die Klassen, die die in den Container geladene Reihenfolge steuern müssen. Beachten Sie, dass Sie nach dem Befolgen des obigen Prozesses keine relevanten Anmerkungen für die gewünschten Konfigurationsklassen verwenden Kontrolle. Was wir im Folgenden beispielsweise kontrollieren wollen, ist, dass die Reihenfolge der OrderService-Klasse im Vordergrund steht.

public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition.setBeanClass(OrderService.class);
        beanDefinitionRegistry.registerBeanDefinition("orderService",beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

Konfigurieren Sie MyAppInitializer in der Datei spring.factories

org.springframework.context.ApplicationContextInitializer=\
com.congge.config.MyAppInitializer

Führen Sie das Hauptprogramm aus und beobachten Sie die Konsolenausgabe wie folgt. Dies zeigt, dass OrderService nicht nur vom Container verwaltet wird, sondern auch die Reihenfolge im Vordergrund steht, wodurch der Zweck der Steuerung der Bohnenreihenfolge erreicht wird.

6. Nutzungsszenarien

Schauen wir uns an dieser Stelle die am Anfang des Artikels gestellte Frage an: Warum müssen wir die Ladereihenfolge der Bohnen im Frühjahr kontrollieren? Bezüglich dieses Problems sind hier die folgenden häufigen Szenarios aufgeführt.

6.1 Bean-Abhängigkeiten auflösen

Während der Initialisierung muss beispielsweise die Konfigurationsklasse B bestimmte Attribute in der Konfigurationsklasse A verwenden, und die Konfigurationsklasse C muss bestimmte Attribute in B verwenden. In diesem Fall von kaskadierenden Abhängigkeiten, um Abhängigkeiten während des Startvorgangs zu vermeiden Wenn ein Startfehler auftritt, lässt sich das Problem beheben, indem die Reihenfolge der Bean-Konfiguration gesteuert wird.

6.2 Legen Sie die höchste Priorität für bestimmte Konfigurationsklassen fest

Beispielsweise haben wir eine Konfigurationsklasse für die Produktverwaltung, die beim Projektstart die Daten aus der Datenbank in den Redis-Cache schreiben und die Produktdaten regelmäßig aktualisieren muss, und diese Klasse bietet auch eine statische Methode für den externen Zugriff. Da in diesem Szenario die statische Methode extern bereitgestellt wird, können andere Klassen ihre Methode direkt aufrufen. Wenn sie nicht zuerst geladen wird und die Produktdaten angefordert werden, wurde das Produkt nicht geladen, und dann treten Probleme auf. Daher müssen Sie sicherstellen, dass diese Konfigurations-Bean zuerst geladen wird.

6.3 Abhängigkeitsübertragung

Wenn das Programm Schnittstellenparameter in unterschiedlichen Gradienten überprüfen und abfangen muss, besteht ein gängiger Ansatz darin, AOP zu verwenden, um Störungen des Hauptgeschäftsprozesses zu reduzieren. Wenn in diesem Fall die AOP-Logikverarbeitung der Klasse A abgeschlossen ist, wird sie weiter übergeben Zum AOP der nächsten Ebene B zur Verarbeitung, was die Steuerung der Ausführungsreihenfolge verschiedener AOP-Klassen erfordert. Zu diesem Zeitpunkt ist es notwendig, die Ausführungsreihenfolge verschiedener AOPs zu steuern.

7. Schreiben Sie am Ende des Artikels

In einigen speziellen Geschäftsszenarien kann uns die vernünftige Steuerung der Ladereihenfolge von Bohnen dabei helfen, viele komplexe Geschäftsanforderungen zu lösen. Es kann auch als funktionaler Erweiterungspunkt verwendet werden, der von Spring bereitgestellt wird und eine wichtige Rolle im Spring-System spielt. Dieser Artikel Das ist das Ende dieses Artikels, danke fürs Zuschauen.

Supongo que te gusta

Origin blog.csdn.net/zhangcongyi420/article/details/133249021
Recomendado
Clasificación