Übersicht über den Lebenszyklus von Spring Beans
Heute werden wir uns den Lebenszyklus von Spring Beans ansehen. Spring Beans sind der wichtigste Teil jeder Spring-Anwendung. Der Spring ApplicationContext ist dafür verantwortlich, die in der Spring-Bean-Konfigurationsdatei definierten Spring Beans zu initialisieren.
Lebenszyklus von Spring Beans
Der Lebenszyklus von Spring BeansDer Spring Context ist auch verantwortlich für das Einbringen von Abhängigkeiten in den Bean, entweder durch Setter- oder Konstruktormethoden oder durch Spring-Autowiring. Manchmal möchten wir Ressourcen in den Bean-Klassen initialisieren, zum Beispiel Datenbankverbindungen erstellen oder Dienste von Drittanbietern zum Zeitpunkt der Initialisierung vor einer Kundenanfrage validieren. Das Spring-Framework bietet verschiedene Möglichkeiten, wie wir Post-Initialisierungs- und Pre-Destroy-Methoden in einem Spring-Bean-Lebenszyklus bereitstellen können.
Implementierung von InitializingBean und DisposableBean
Durch die Implementierung der Schnittstellen InitializingBean und DisposableBean – Beide Schnittstellen deklarieren eine einzelne Methode, in der wir Ressourcen im Bean initialisieren/schließen können. Für die Post-Initialisierung können wir die Schnittstelle InitializingBean implementieren und eine Implementierung der Methode afterPropertiesSet() bereitstellen. Für Pre-Destroy können wir die Schnittstelle DisposableBean implementieren und eine Implementierung der Methode destroy() bereitstellen. Diese Methoden sind die Rückrufmethoden und ähnlich wie Servlet-Listener-Implementierungen. Dieser Ansatz ist einfach zu verwenden, wird aber nicht empfohlen, da er eine enge Kopplung mit dem Spring-Framework in unseren Bean-Implementierungen erzeugt.
Bereitstellung von init-method und destroy-method Attributwerten für den Bean in der Spring-Bean-Konfigurationsdatei. Dies ist der empfohlene Ansatz, da keine direkte Abhängigkeit vom Spring-Framework besteht und wir unsere eigenen Methoden erstellen können.
Beachten Sie, dass sowohl Post-Init- als auch Pre-Destroy-Methoden keine Argumente haben sollten, aber sie können Ausnahmen auslösen. Wir müssten auch die Bean-Instanz aus dem Spring-Anwendungskontext für diese Methodenaufrufe erhalten.
Spring Bean-Lebenszyklus – @PostConstruct, @PreDestroy Annotationen
Das Spring-Framework unterstützt auch die Annotationen @PostConstruct und @PreDestroy zur Definition von Post-Init- und Pre-Destroy-Methoden. Diese Annotationen sind Teil des Pakets javax.annotation. Damit diese Annotationen funktionieren, müssen wir unsere Spring-Anwendung so konfigurieren, dass sie nach Annotationen sucht. Wir können dies entweder durch Definieren eines Beans des Typs org.springframework.context.annotation.CommonAnnotationBeanPostProcessor oder durch das Element context:annotation-config in der Spring-Bean-Konfigurationsdatei tun. Lassen Sie uns eine einfache Spring-Anwendung erstellen, um die Verwendung der oben genannten Konfigurationen für das Management des Spring-Bean-Lebenszyklus zu demonstrieren. Erstellen Sie ein Spring Maven-Projekt in Spring Tool Suite, das endgültige Projekt wird wie folgt aussehen.
Spring Bean-Lebenszyklus – Maven-Abhängigkeiten
Wir müssen keine zusätzlichen Abhängigkeiten für die Konfiguration der Spring-Bean-Lebenszyklusmethoden einschließen, unsere pom.xml-Datei ist wie jede andere Standard-Spring-Maven-Projekt.
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringBeanLifeCycle</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generische Eigenschaften -->
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.0.2.RELEASE</spring-framework.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties>
<dependencies>
<!-- Spring und Transaktionen -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging mit SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
Spring Bean-Lebenszyklus – Modelklasse
Lassen Sie uns eine einfache Java-Bean-Klasse erstellen, die in Dienstklassen verwendet wird.
package com.journaldev.spring.bean;
public class Employee {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Spring Bean-Lebenszyklus – InitializingBean, DisposableBean
Lassen Sie uns eine Dienstklasse erstellen, in der wir sowohl die InitializingBean- als auch die DisposableBean-Schnittstellen für Post-Initialisierungs- und Pre-Zerstörungsmethoden implementieren werden.
package com.journaldev.spring.service;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import com.journaldev.spring.bean.Employee;
public class EmployeeService implements InitializingBean, DisposableBean {
private Employee employee;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public EmployeeService() {
System.out.println("EmployeeService no-args constructor called");
}
@Override
public void destroy() throws Exception {
System.out.println("EmployeeService Closing resources");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("EmployeeService initializing to dummy value");
if(employee.getName() == null) {
employee.setName("Pankaj");
}
}
}
Spring Bean-Lebenszyklus – Benutzerdefinierte Post-Init, Pre-Destroy
Da wir nicht möchten, dass unsere Dienste eine direkte Abhängigkeit vom Spring-Framework haben, erstellen wir eine weitere Form der Employee Service-Klasse, in der wir Post-Init- und Pre-Destroy-Spring-Lebenszyklusmethoden haben werden und wir werden diese in der Spring-Bean-Konfigurationsdatei konfigurieren.
package com.journaldev.spring.service;
import com.journaldev.spring.bean.Employee;
public class MyEmployeeService {
private Employee employee;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public MyEmployeeService() {
System.out.println("MyEmployeeService no-args constructor called");
}
// Pre-Destroy-Methode
public void destroy() throws Exception {
System.out.println("MyEmployeeService Closing resources");
}
// Post-Init-Methode
public void init() throws Exception {
System.out.println("MyEmployeeService initializing to dummy value");
if(employee.getName() == null) {
employee.setName("Pankaj");
}
}
}
Spring Bean-Lebenszyklus – @PostConstruct, @PreDestroy
Nachfolgend eine einfache Klasse, die als Spring Bean konfiguriert wird und für Post-Init- und Pre-Destroy-Methoden verwenden wir die Annotationen @PostConstruct und @PreDestroy.
package com.journaldev.spring.service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyService {
@PostConstruct
public void init() {
System.out.println("MyService init method called");
}
public MyService() {
System.out.println("MyService no-args constructor called");
}
@PreDestroy
public void destroy() {
System.out.println("MyService destroy method called");
}
}
Spring Bean-Lebenszyklus – Konfigurationsdatei
Lassen Sie uns sehen, wie wir unsere Beans in der Spring-Kontextdatei konfigurieren.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Nicht initialisierende Mitarbeitername-Variable -->
<bean name="employee" class="com.journaldev.spring.bean.Employee" />
<bean name="employeeService" class="com.journaldev.spring.service.EmployeeService">
<property name="employee" ref="employee"></property>
</bean>
<bean name="myEmployeeService" class="com.journaldev.spring.service.MyEmployeeService"
init-method="init" destroy-method="destroy">
<property name="employee" ref="employee"></property>
</bean>
<!-- Die Initialisierung von CommonAnnotationBeanPostProcessor ist gleich wie context:annotation-config -->
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<bean name="myService" class="com.journaldev.spring.service.MyService" />
</beans>
Spring Bean-Lebenszyklus – Testprogramm
Lassen Sie uns betrachten, wie wir den Spring-Bean-Lebenszyklus in einer praktischen Anwendung testen können. Nachfolgend das Testprogramm, das die Verwendung der diskutierten Konzepte demonstriert.
package com.journaldev.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.spring.service.EmployeeService;
import com.journaldev.spring.service.MyEmployeeService;
public class SpringMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
System.out.println("Spring Context initialized");
// MyEmployeeService service = ctx.getBean("myEmployeeService", MyEmployeeService.class);
EmployeeService service = ctx.getBean("employeeService", EmployeeService.class);
System.out.println("Bean retrieved from Spring Context");
System.out.println("Employee Name=" + service.getEmployee().getName());
ctx.close();
System.out.println("Spring Context Closed");
}
}
Wenn wir das obige Testprogramm ausführen, erhalten wir die folgende Ausgabe:
Apr 01, 2014 10:50:50 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Aktualisierung org.springframework.context.support.ClassPathXmlApplicationContext@c1b9b03: Startdatum [Tue Apr 01 22:50:50 PDT 2014]; Wurzel der Kontexthierarchie
Apr 01, 2014 10:50:50 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Laden von XML-Bean-Definitionen aus der Klassenpfad-Ressource [spring.xml]
EmployeeService no-args constructor called
EmployeeService initializing to dummy value
MyEmployeeService no-args constructor called
MyEmployeeService initializing to dummy value
MyService no-args constructor called
MyService init method called
Spring Context initialized
Bean retrieved from Spring Context
Employee Name=Pankaj
Apr 01, 2014 10:50:50 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Schließen org.springframework.context.support.ClassPathXmlApplicationContext@c1b9b03: Startdatum [Tue Apr 01 22:50:50 PDT 2014]; Wurzel der Kontexthierarchie
MyService destroy method called
MyEmployeeService Closing resources
EmployeeService Closing resources
Spring Context Closed
Wichtige Punkte zum Spring Bean-Lebenszyklus:
- Aus der Konsolenausgabe geht klar hervor, dass der Spring Context zuerst den No-Args-Konstruktor verwendet, um das Bean-Objekt zu initialisieren, und dann die Post-Init-Methode aufruft.
- Die Reihenfolge der Bean-Initialisierung ist dieselbe wie in der Spring-Bean-Konfigurationsdatei definiert.
- Der Kontext wird erst zurückgegeben, wenn alle Spring Beans ordnungsgemäß mit den Post-Init-Methoden initialisiert sind.
- Der Mitarbeitername wird als „Pankaj“ ausgegeben, weil er in der Post-Init-Methode initialisiert wurde.
- Wenn der Kontext geschlossen wird, werden die Beans in umgekehrter Reihenfolge zerstört, in der sie initialisiert wurden, d.h. in LIFO (Last-In-First-Out) Reihenfolge.
- Sie können den Code auskommentieren, um das Bean vom Typ MyEmployeeService zu erhalten und bestätigen, dass die Ausgabe ähnlich sein wird und alle oben genannten Punkte befolgt.
Spring Aware Schnittstellen
Manchmal benötigen wir Spring Framework-Objekte in unseren Beans, um bestimmte Operationen durchzuführen, zum Beispiel ServletConfig- und ServletContext-Parameter zu lesen oder um die vom ApplicationContext geladenen Bean-Definitionen zu kennen. Deshalb bietet das Spring-Framework eine Reihe von *Aware-Schnittstellen, die wir in unseren Bean-Klassen implementieren können. org.springframework.beans.factory.Aware ist die Marker-Oberfläche für all diese Aware-Schnittstellen. Alle *Aware-Schnittstellen sind Unteroberflächen von Aware und deklarieren eine einzige Setter-Methode, die vom Bean implementiert werden muss. Dann verwendet der Spring-Kontext setter-basierte Abhängigkeitsinjektion, um die entsprechenden Objekte im Bean einzufügen und für unsere Verwendung verfügbar zu machen. Spring Aware-Schnittstellen sind ähnlich wie Servlet-Listener mit Rückrufmethoden und implementieren das Beobachter-Entwurfsmuster. Einige der wichtigen Aware-Schnittstellen sind:
- ApplicationContextAware – um das ApplicationContext-Objekt einzufügen, Beispielverwendung ist die Abfrage des Arrays der Bean-Definitionsnamen.
- BeanFactoryAware – um das BeanFactory-Objekt einzufügen, Beispielverwendung ist die Überprüfung des Scopes eines Beans.
- BeanNameAware – um den in der Konfigurationsdatei definierten Bean-Namen zu kennen.
- ResourceLoaderAware – um das ResourceLoader-Objekt einzufügen, Beispielverwendung ist das Abrufen des Eingabestreams für eine Datei im Klassenpfad.
- ServletContextAware – um das ServletContext-Objekt in einer MVC-Anwendung einzufügen, Beispielverwendung ist das Lesen von Kontextparametern und -attributen.
- ServletConfigAware – um das ServletConfig-Objekt in einer MVC-Anwendung einzufügen, Beispielverwendung ist das Abrufen von Servlet-Konfigurationsparametern.
Implementierung von Spring Aware-Schnittstellen
Lassen Sie uns die Verwendung dieser Aware-Schnittstellen in Aktion sehen, indem wir einige davon in einer Klasse implementieren, die wir als Spring Bean konfigurieren werden.
package com.journaldev.spring.service;
import java.util.Arrays;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
public class MyAwareService implements ApplicationContextAware,
ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,
BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware {
@Override
public void setApplicationContext(ApplicationContext ctx)
throws BeansException {
System.out.println("setApplicationContext called");
System.out.println("setApplicationContext:: Bean-Definitionsnamen="
+ Arrays.toString(ctx.getBeanDefinitionNames()));
}
@Override
public void setBeanName(String beanName) {
System.out.println("setBeanName called");
System.out.println("setBeanName:: Bean-Name definiert im Kontext="
+ beanName);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("setBeanClassLoader called");
System.out.println("setBeanClassLoader:: Klassenladername="
+ classLoader.getClass().getName());
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
System.out.println("setResourceLoader called");
Resource resource = resourceLoader.getResource("classpath:spring.xml");
System.out.println("setResourceLoader:: Ressourcendateiname="
+ resource.getFilename());
}
@Override
public void setImportMetadata(AnnotationMetadata annotationMetadata) {
System.out.println("setImportMetadata called");
}
@Override
public void setEnvironment(Environment env) {
System.out.println("setEnvironment called");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("setBeanFactory called");
System.out.println("setBeanFactory:: Mitarbeiter-Bean Singleton="
+ beanFactory.isSingleton("employee"));
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
System.out.println("setApplicationEventPublisher called");
}
}
Spring *Aware-Beispiel-Konfigurationsdatei
Sehr einfache Spring-Bean-Konfigurationsdatei.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="employee" class="com.journaldev.spring.bean.Employee" />
<bean name="myAwareService" class="com.journaldev.spring.service.MyAwareService" />
</beans>
Spring *Aware-Testprogramm
Jetzt führen wir die obige Klasse aus und sehen uns die Ausgabe an.
package com.journaldev.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.spring.service.MyAwareService;
public class SpringAwareMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aware.xml");
ctx.getBean("myAwareService", MyAwareService.class);
ctx.close();
}
}
Das Ausführen der obigen Klasse erzeugt die folgende Ausgabe:
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Aktualisierung org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: Startdatum [Tue Apr 01 23:27:05 PDT 2014]; Wurzel der Kontexthierarchie
Apr 01, 2014 11:27:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Laden von XML-Bean-Definitionen aus der Klassenpfad-Ressource [spring-aware.xml]
setBeanName called
setBeanName:: Bean-Name definiert im Kontext=myAwareService
setBeanClassLoader called
setBeanClassLoader:: Klassenlader-Name=sun.misc.Launcher$AppClassLoader
setBeanFactory called
setBeanFactory:: Mitarbeiter-Bean Singleton=true
setEnvironment called
setResourceLoader called
setResourceLoader:: Ressourcendateiname=spring.xml
setApplicationEventPublisher called
setApplicationContext called
setApplicationContext:: Bean-Definitionsnamen=[employee, myAwareService]
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Schließen org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: Startdatum [Tue Apr 01 23:27:05 PDT 2014]; Wurzel der Kontexthierarchie
Die Konsolenausgabe des Testprogramms ist einfach zu verstehen, ich werde nicht im Detail darauf eingehen. Das ist alles zu den Spring Bean-Lebenszyklusmethoden und der Injektion von Framework-spezifischen Objekten in die Spring Beans – eine Anleitung.