Ausführen der Setup-Daten beim Start in Spring

Ausführen von Setup-Daten beim Start im Frühjahr

1. Einführung

In diesem Artikel konzentrieren wir uns aufrun logic at the startup of a Spring application.

Weitere Lektüre:

Konfigurieren Sie eine Spring Boot-Webanwendung

Einige der nützlicheren Konfigurationen für eine Spring Boot-Anwendung.

Read more

Spring Boot: Konfigurieren einer Hauptklasse

Erfahren Sie, wie Sie die Hauptklasse Ihrer Spring Boot-Anwendung in Maven und Gradle konfigurieren.

Read more

2. Ausführen der Logik beim Start

Das Ausführen von Logik während / nach dem Start der Spring-Anwendung ist ein häufiges Szenario, das jedoch mehrere Probleme verursacht.

Um von Inverse of Control zu profitieren, müssen wir natürlich auf eine teilweise Kontrolle über den Anwendungsfluss zum Container verzichten. Aus diesem Grund müssen Instanziierung, Setup-Logik beim Start usw. besonders berücksichtigt werden.

Wir können unsere Logik nach der Instanziierung eines Objekts nicht einfach in die Konstruktoren oder Aufrufmethoden der Beans aufnehmen. Während dieser Prozesse haben wir einfach keine Kontrolle.

Schauen wir uns das Beispiel aus dem wirklichen Leben an:

@Component
public class InvalidInitExampleBean {

    @Autowired
    private Environment env;

    public InvalidInitExampleBean() {
        env.getActiveProfiles();
    }
}

Hier versuchen wir, auf einautowired-Feld im Konstruktor zuzugreifen. Beim Aufruf des Konstruktors ist das Spring Bean noch nicht vollständig initialisiert. Dies ist problematisch, weilcalling not yet initialized fields will of course result in NullPointerExceptions.

Der Frühling gibt uns einige Möglichkeiten, mit dieser Situation umzugehen.

2.1. Die Annotation@PostConstruct

Die Annotation@PostConstructvon Javax kann zum Annotieren einer Methode verwendet werden, dieonce immediately after the bean’s initialization ausführen soll. Beachten Sie, dass die mit Annotationen versehene Methode von Spring ausgeführt wird, auch wenn nichts zu injizieren ist.

Hier sind@PostConstruct in Aktion:

@Component
public class PostConstructExampleBean {

    private static final Logger LOG
      = Logger.getLogger(PostConstructExampleBean.class);

    @Autowired
    private Environment environment;

    @PostConstruct
    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

Im obigen Beispiel können Sie sehen, dass die Instanz vonEnvironmenticher injiziert und dann in der mit Anmerkungen versehenen Methode@PostConstructaufgerufen wurde, ohneNullPointerException zu werfen.

2.2. DieInitializingBean-Schnittstelle

Der Ansatz vonInitializingBeanfunktioniert ziemlich ähnlich wie der vorherige. Anstatt eine Methode mit Anmerkungen zu versehen, müssen Sie die SchnittstelleInitializingBeanund die MethodeafterPropertiesSet()implementieren.

Hier sehen Sie das vorherige Beispiel, das über dieInitializingBean-Schnittstelle implementiert wurde:

@Component
public class InitializingBeanExampleBean implements InitializingBean {

    private static final Logger LOG
      = Logger.getLogger(InitializingBeanExampleBean.class);

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.3. EinApplicationListener

Dieser Ansatz kann fürrunning logic after the Spring context has been initialized verwendet werden, sodass wir uns nicht auf eine bestimmte Bean konzentrieren, sondern darauf warten, dass alle initialisiert werden.

Um dies zu erreichen, müssen Sie eine Bean erstellen, die dieApplicationListener<ContextRefreshedEvent>-Schnittstelle implementiert:

@Component
public class StartupApplicationListenerExample implements
  ApplicationListener {

    private static final Logger LOG
      = Logger.getLogger(StartupApplicationListenerExample.class);

    public static int counter;

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

Die gleichen Ergebnisse können mit der neu eingeführten Annotation@EventListenererzielt werden:

@Component
public class EventListenerExampleBean {

    private static final Logger LOG
      = Logger.getLogger(EventListenerExampleBean.class);

    public static int counter;

    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

In diesem Beispiel haben wirContextRefreshedEvent.ausgewählt. Stellen Sie sicher, dass Sie ein geeignetes Ereignis auswählen, das Ihren Anforderungen entspricht.

2.4. Das Initmethod-Attribut@Bean

Die Eigenschaft initMethod kann verwendet werden, um eine Methode nach der Initialisierung einer Bean auszuführen.

So sieht eine Bohne aus:

public class InitMethodExampleBean {

    private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class);

    @Autowired
    private Environment environment;

    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

Sie können feststellen, dass weder spezielle Schnittstellen implementiert noch spezielle Anmerkungen verwendet werden.

Dann können wir die Bean mithilfe der Annotation@Beandefinieren:

@Bean(initMethod="init")
public InitMethodExampleBean exBean() {
    return new InitMethodExampleBean();
}

Und so sieht eine Bean-Definition in einer XML-Konfiguration aus:


2.5. Konstruktor-Injection

Wenn Sie Felder mit Constructor Injection einfügen, können Sie Ihre Logik einfach in einen Konstruktor einfügen:

@Component
public class LogicInConstructorExampleBean {

    private static final Logger LOG
      = Logger.getLogger(LogicInConstructorExampleBean.class);

    private final Environment environment;

    @Autowired
    public LogicInConstructorExampleBean(Environment environment) {
        this.environment = environment;
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.6. Spring BootCommandLineRunner

Spring Boot bietet eineCommanLineRunner-Schnittstelle mit einer Callbackrun()-S-Methode, die beim Start der Anwendung aufgerufen werden kann, nachdem der Spring-Anwendungskontext instanziiert wurde.

Schauen wir uns ein Beispiel an:

@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
    private static final Logger LOG =
      LoggerFactory.getLogger(CommandLineAppStartupRunner.class);

    public static int counter;

    @Override
    public void run(String...args) throws Exception {
        LOG.info("Increment counter");
        counter++;
    }
}

Den vollständigen Quellcode finden Sie aufGithub.

Note: Wie indocumentation erwähnt, können mehrereCommandLineRunner-Beans im selben Anwendungskontext definiert und über die@Ordered-Schnittstelle oder die@Order-Annotation sortiert werden .

2.7. Spring BootApplicationRunner

Ähnlich wie beiCommandLineRunner,bietet Spring Boot auch eineApplicationRunner-Schnittstelle mit einerrun()-S-Methode, die beim Start der Anwendung aufgerufen werden kann. Anstelle der Argumente von rawString, die an die Rückrufmethode übergeben wurden, haben wir jedoch eine Instanz der KlasseApplicationArguments.

DieApplicationArguments-Schnittstelle verfügt über Methoden zum Abrufen von Argumentwerten, bei denen es sich um Optionen und einfache Argumentwerte handelt. Ein Argument mit dem Präfix - - ist ein Optionsargument.

Schauen wir uns ein Beispiel an:

@Component
public class AppStartupRunner implements ApplicationRunner {
    private static final Logger LOG =
      LoggerFactory.getLogger(AppStartupRunner.class);

    public static int counter;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        LOG.info("Application started with option names : {}",
          args.getOptionNames());
        LOG.info("Increment counter");
        counter++;
    }
}

Den vollständigen Quellcode finden Sie aufGithub.

3. Mechanismen kombinieren

Um die volle Kontrolle über Ihre Bohnen zu erlangen, sollten Sie die oben genannten Mechanismen miteinander kombinieren.

Die Reihenfolge der Ausführung ist wie folgt:

  1. Der Erbauer

  2. die mit@PostConstruct annotierten Methoden

  3. dieafterPropertiesSet()-Methode von InitializingBean

  4. Die Initialisierungsmethode, die in XML alsinit-method angegeben ist

Erstellen wir eine Spring Bean, die alle Mechanismen kombiniert:

@Component
@Scope(value = "prototype")
public class AllStrategiesExampleBean implements InitializingBean {

    private static final Logger LOG
      = Logger.getLogger(AllStrategiesExampleBean.class);

    public AllStrategiesExampleBean() {
        LOG.info("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info("InitializingBean");
    }

    @PostConstruct
    public void postConstruct() {
        LOG.info("PostConstruct");
    }

    public void init() {
        LOG.info("init-method");
    }
}

Wenn Sie versuchen, diese Bean zu instanziieren, werden Protokolle angezeigt, die der oben angegebenen Reihenfolge entsprechen:

[main] INFO o.b.startup.AllStrategiesExampleBean - Constructor
[main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct
[main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean
[main] INFO o.b.startup.AllStrategiesExampleBean - init-method

4. Fazit

In diesem Artikel haben wir verschiedene Möglichkeiten zum Ausführen von Logik beim Start der Spring-Anwendung veranschaulicht.

Codebeispiele finden Sie aufGitHub.