Anleitung zum Ausführen von Logic beim Start im Frühjahr

1. Einleitung

In diesem Artikel konzentrieren wir uns darauf, wie Logik beim Start einer Spring-Anwendung ausgeführt wird .

2. Ausführen von Logic 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 die teilweise Kontrolle über den Fluss der Anwendung 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 ein automatisch verdrahtetes Feld im Konstruktor zuzugreifen . Wenn der Konstruktor aufgerufen wird, ist die Spring Bean noch nicht vollständig initialisiert. Dies ist problematisch, da das Aufrufen noch nicht initialisierter Felder natürlich zu NullPointerException s führt .

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

2.1. Die @ PostConstruct- Annotation

Die @ PostConstruct- Annotation von Javax kann zum Annotieren einer Methode verwendet werden, die unmittelbar nach der Initialisierung der Bean einmal ausgeführt werden soll . Beachten Sie, dass die mit Anmerkungen versehene Methode von Spring ausgeführt wird, auch wenn nichts zu injizieren ist.

Hier ist @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 Umgebungsinstanz sicher injiziert und dann in der mit @PostConstruct annotierten Methode aufgerufen wurde , ohne eine NullPointerException auszulösen .

2.2. Die InitializingBean- Schnittstelle

Der InitializingBean- Ansatz funktioniert ziemlich ähnlich wie der vorherige. Anstatt eine Methode mit Anmerkungen zu versehen, müssen Sie die InitializingBean- Schnittstelle und die afterPropertiesSet () -Methode implementieren .

Hier sehen Sie das vorherige Beispiel, das mithilfe der InitializingBean- 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. Ein ApplicationListener

Dieser Ansatz kann zum Ausführen von Logik verwendet werden, nachdem der Spring-Kontext initialisiert wurde. Wir konzentrieren uns also nicht auf eine bestimmte Bean, sondern warten darauf, dass alle initialisiert werden.

Um dies zu erreichen, müssen Sie eine Bean erstellen, die die ApplicationListener- 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 @EventListener erzielt 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 wir das ContextRefreshedEvent ausgewählt. Stellen Sie sicher, dass Sie eine geeignete Veranstaltung auswählen, die Ihren Anforderungen entspricht.

2.4. Das @ Bean Initmethod-Attribut

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.

Anschließend können wir die Bean mithilfe der Annotation @Bean definieren :

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

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

2.5. Konstruktorinjektion

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 Boot CommandLineRunner

Spring Boot bietet einer CommandLineRunner- Schnittstelle eine Callback- Methode run () , 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++; } }

Hinweis : Wie in der Dokumentation erwähnt, können mehrere CommandLineRunner- Beans im selben Anwendungskontext definiert und über die @ Ordered- Schnittstelle oder die @ Order- Annotation bestellt werden.

2.7. Spring Boot ApplicationRunner

Ähnlich wie CommandLineRunner bietet Spring Boot auch eine ApplicationRunner- Schnittstelle mit einer run () -Methode, die beim Start der Anwendung aufgerufen werden kann. Anstelle von rohen String- Argumenten, die an die Callback-Methode übergeben werden, haben wir jedoch eine Instanz der ApplicationArguments- Klasse.

The ApplicationArguments interface has methods to get argument values that are options and plain argument values. An argument that is prefixed with – – is an option argument.

Let's look at an example:

@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++; } }

3. Combining Mechanisms

In order to achieve full control over your beans, you might want to combine the above mechanisms together.

The order of execution is as follows:

  1. The constructor
  2. the @PostConstruct annotated methods
  3. the InitializingBean's afterPropertiesSet() method
  4. the initialization method specified as init-method in XML

Let's create a Spring bean that combines all mechanisms:

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

If you try to instantiate this bean, you will be able to see logs that match the order specified above:

[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. Conclusion

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

Codebeispiele finden Sie auf GitHub.