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 Spring-Kontext ist auch dafür verantwortlich, Abhängigkeiten in die Bean einzuspeisen, entweder über Setter- oder Konstruktormethoden oder durch Spring-Autowiring. Manchmal möchten wir Ressourcen in den Bean-Klassen initialisieren, zum Beispiel Datenbankverbindungen erstellen oder Drittanbieterdienste beim Initialisieren vor einer Clientanfrage validieren. Das Spring-Framework bietet verschiedene Möglichkeiten, wie wir Post-Initialisierungs- und Pre-Destroy-Methoden im Lebenszyklus einer Spring-Bean bereitstellen können.
- Durch Implementierung der Schnittstellen InitializingBean und DisposableBean – Beide Schnittstellen deklarieren eine einzige Methode, in der wir Ressourcen im Bean initialisieren/schließen können. Für die Nachinitialisierung können wir das Interface
InitializingBean
implementieren und die MethodeafterPropertiesSet()
bereitstellen. Für die Vorzerstörung können wir das InterfaceDisposableBean
implementieren und die Methodedestroy()
bereitstellen. Diese Methoden sind Rückrufmethoden und ähnlich wie Servlet-Listener-Implementierungen. Dieser Ansatz ist einfach zu verwenden, aber es wird nicht empfohlen, da er eine enge Kopplung mit dem Spring-Framework in unseren Bean-Implementierungen erzeugt. - Bereitstellung von Werten für die Attribute init-method und destroy-method für das Bean in der Spring-Bean-Konfigurationsdatei. Dies ist der empfohlene Ansatz aufgrund fehlender direkter Abhängigkeit vom Spring-Framework und wir können unsere eigenen Methoden erstellen.
Beachten Sie, dass sowohl die Nachinitialisierung– als auch die Vorzerstörungsmethoden keine Argumente haben sollten, aber Ausnahmen auslösen können. Wir benötigen auch die Bean-Instanz aus dem Spring-Anwendungskontext für diese Methodenaufrufe.
Lebenszyklus des Spring Beans – @PostConstruct, @PreDestroy Annotations
Das Spring-Framework unterstützt auch die Annotationen @PostConstruct
und @PreDestroy
zur Definition von Post-Init- und Pre-Destroy-Methoden. Diese Annotationen gehören zum Paket javax.annotation
. Damit diese Annotationen funktionieren, müssen wir unsere Spring-Anwendung so konfigurieren, dass sie nach Annotationen sucht. Dies können wir entweder durch die Definition einer Bean vom Typ org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
oder durch das context:annotation-config
-Element in der Spring-Bean-Konfigurationsdatei tun. Lassen Sie uns eine einfache Spring-Anwendung schreiben, um die Verwendung der oben genannten Konfigurationen für das Lebenszyklusmanagement von Spring-Beans zu demonstrieren. Erstellen Sie ein Spring-Maven-Projekt in der Spring Tool Suite. Das endgültige Projekt wird wie auf dem Bild unten aussehen.
Spring Bean Life Cycle – Maven Dependencies
Wir müssen keine zusätzlichen Abhängigkeiten für die Konfiguration der Lebenszyklusmethoden von Spring-Beans hinzufügen. Unsere pom.xml-Datei sieht aus wie bei jedem anderen 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>
<!-- Generic properties -->
<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 and Transactions -->
<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 with 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 Life Cycle – Modellklasse
Lassen Sie uns eine einfache Java Bean-Klasse erstellen, die in Service-Klassen 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 Life Cycle – InitializingBean, DisposableBean
Erstellen wir eine Service-Klasse, in der wir beide Schnittstellen für Post-Init- und Pre-Destroy-Methoden 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 Life Cycle – Benutzerdefinierte Post-Init, Pre-Destroy
Weil wir nicht möchten, dass unsere Dienste eine direkte Abhängigkeit vom Spring-Framework haben, erstellen wir eine andere Form einer Employee Service-Klasse, in der wir Post-Init- und Pre-Destroy-Spring-Lebenszyklusmethoden haben und diese in der Spring Bean-Konfigurationsdatei konfigurieren werden.
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");
}
}
}
Wir werden uns die Spring Bean-Konfigurationsdatei gleich ansehen. Bevor wir das tun, erstellen wir eine weitere Service-Klasse, die die Annotationen @PostConstruct und @PreDestroy verwendet.
Spring Bean Lebenszyklus – @PostConstruct, @PreDestroy
Unten ist eine einfache Klasse, die als Spring-Bean konfiguriert wird, und für die Post-Init- und Pre-Destroy-Methoden verwenden wir die @PostConstruct– und @PreDestroy-Annotationen.
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 destory(){
System.out.println("MyService destroy method called");
}
}
Spring Bean Lebenszyklus – Konfigurationsdatei
Lassen Sie uns sehen, wie wir unsere Beans in der Spring-Kontextdatei konfigurieren werden.
<?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">
<!-- Not initializing employee name 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>
<!-- initializing CommonAnnotationBeanPostProcessor is same as context:annotation-config -->
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<bean name="myService" class="com.journaldev.spring.service.MyService" />
</beans>
Beachten Sie, dass ich den Mitarbeitername nicht in seiner Bean-Definition initialisiere. Da EmployeeService Schnittstellen verwendet, benötigen wir hier keine spezielle Konfiguration. Für die MyEmployeeService-Bean verwenden wir die init-method und destroy-method Attribute, um dem Spring-Framework unsere benutzerdefinierten Methoden mitzuteilen, die ausgeführt werden sollen. Die Konfiguration der MyService-Bean hat nichts Besonderes, aber wie Sie sehen können, aktiviere ich hier die konfigurationsbasierte Annotation. Unsere Anwendung ist bereit, lassen Sie uns ein Testprogramm schreiben, um zu sehen, wie verschiedene Methoden ausgeführt werden.
Frühjahrsbohnenlebenszyklus – Testprogramm
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");
}
}
Beim Ausführen des obigen Testprogramms erhalten wir die folgende Ausgabe.
Apr 01, 2014 10:50:50 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c1b9b03: startup date [Tue Apr 01 22:50:50 PDT 2014]; root of context hierarchy
Apr 01, 2014 10:50:50 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [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: Closing org.springframework.context.support.ClassPathXmlApplicationContext@c1b9b03: startup date [Tue Apr 01 22:50:50 PDT 2014]; root of context hierarchy
MyService destroy method called
MyEmployeeService Closing resources
EmployeeService Closing resources
Spring Context Closed
Wichtige Punkte zum Frühjahrsbohnenlebenszyklus:
- Der Konsolenausgabe ist zu entnehmen, dass der Spring-Kontext zuerst den Bean-Objekt mit dem no-args-Konstruktor initialisiert und dann die Post-Init-Methode aufruft.
- Die Reihenfolge der Bean-Initialisierung entspricht der im Spring Bean-Konfigurationsdatei definierten Reihenfolge.
- Der Kontext wird erst zurückgegeben, wenn alle Spring-Beans ordnungsgemäß mit Post-Init-Methoden initialisiert wurden.
- Der Mitarbeitername wird als „Pankaj“ gedruckt, da er in der Post-Init-Methode initialisiert wurde.
- Beim Schließen des Kontexts werden die Beans in umgekehrter Reihenfolge zerstört, in der sie initialisiert wurden, d. h. in Last-In-First-Out (LIFO)-Reihenfolge.
Sie können den Code uncommenten, um einen Bean vom Typ MyEmployeeService
zu erhalten und zu bestätigen, dass die Ausgabe ähnlich ist und alle oben genannten Punkte befolgt.
Spring Aware-Schnittstellen
Manchmal benötigen wir Spring Framework-Objekte in unseren Beans, um einige Operationen durchzuführen, zum Beispiel das Lesen von ServletConfig- und ServletContext-Parametern oder um die Bean-Definitionen zu kennen, die vom ApplicationContext geladen wurden. Deshalb stellt das Spring Framework eine Reihe von *Aware-Schnittstellen zur Verfügung, die wir in unseren Bean-Klassen implementieren können. org.springframework.beans.factory.Aware
ist die Wurfschnittstelle für all diese Aware-Schnittstellen. Alle *Aware-Schnittstellen sind Unterschnittstellen von Aware und deklarieren eine einzelne Setter-Methode, die von der Bean implementiert werden soll. Dann verwendet der Spring-Kontext die injektionsbasierte Dependency Injection, um die entsprechenden Objekte in die Bean einzufügen und für unsere Verwendung verfügbar zu machen. Spring Aware-Schnittstellen ähneln Servlet-Listenern mit Rückrufmethoden und implementieren das Beobachter-Entwurfsmuster. Einige der wichtigen Aware-Schnittstellen sind:
- ApplicationContextAware – um das ApplicationContext-Objekt einzufügen, Beispielverwendung ist das Abrufen des Arrays der Bean-Definitionsnamen.
- BeanFactoryAware – um das BeanFactory-Objekt einzufügen, Beispielverwendung ist das Überprüfen des Scopes einer Bean.
- BeanNameAware – um den im Konfigurationsdatei definierten Bean-Namen zu kennen.
- ResourceLoaderAware – um das ResourceLoader-Objekt einzufügen, ein Beispiel dafür ist das Abrufen des Eingabestreams für eine Datei im Klassenpfad.
- ServletContextAware – um das ServletContext-Objekt in einer MVC-Anwendung einzufügen, ein Beispiel dafür ist das Lesen von Kontextparametern und -attributen.
- ServletConfigAware – um das ServletConfig-Objekt in einer MVC-Anwendung einzufügen, ein Beispiel dafür ist das Abrufen von Servlet-Konfigurationsparametern.
Lassen Sie uns sehen, wie diese Aware-Schnittstellen in Aktion verwendet werden, indem wir einige von ihnen in einer Klasse implementieren, die wir als Spring-Bean konfigurieren.
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 Definition Names="
+ Arrays.toString(ctx.getBeanDefinitionNames()));
}
@Override
public void setBeanName(String beanName) {
System.out.println("setBeanName called");
System.out.println("setBeanName:: Bean Name defined in context="
+ beanName);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("setBeanClassLoader called");
System.out.println("setBeanClassLoader:: ClassLoader Name="
+ 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:: Resource File Name="
+ 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:: employee bean singleton="
+ beanFactory.isSingleton("employee"));
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
System.out.println("setApplicationEventPublisher called");
}
}
Spring *Aware Beispielkonfigurationsdatei
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
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();
}
}
Wenn wir jetzt die obige Klasse ausführen, erhalten wir folgende Ausgabe.
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: startup date [Tue Apr 01 23:27:05 PDT 2014]; root of context hierarchy
Apr 01, 2014 11:27:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-aware.xml]
setBeanName called
setBeanName:: Bean Name defined in context=myAwareService
setBeanClassLoader called
setBeanClassLoader:: ClassLoader Name=sun.misc.Launcher$AppClassLoader
setBeanFactory called
setBeanFactory:: employee bean singleton=true
setEnvironment called
setResourceLoader called
setResourceLoader:: Resource File Name=spring.xml
setApplicationEventPublisher called
setApplicationContext called
setApplicationContext:: Bean Definition Names=[employee, myAwareService]
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: startup date [Tue Apr 01 23:27:05 PDT 2014]; root of context hierarchy
Die Konsolenausgabe des Testprogramms ist einfach zu verstehen, ich werde darauf nicht näher eingehen. Das ist alles für die Lebenszyklusmethoden der Spring-Beans und das Einfügen frameworkspezifischer Objekte in die Spring-Beans. Bitte laden Sie das Beispielsprojekt über den unten stehenden Link herunter und analysieren Sie es, um mehr darüber zu erfahren.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-life-cycle