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.
Spring Boot: Konfigurieren einer Hauptklasse
Erfahren Sie, wie Sie die Hauptklasse Ihrer Spring Boot-Anwendung in Maven und Gradle konfigurieren.
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:
-
Der Erbauer
-
die mit@PostConstruct annotierten Methoden
-
dieafterPropertiesSet()-Methode von InitializingBean
-
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.