Hoje vamos analisar o Ciclo de Vida do Bean Spring. Os Beans Spring são a parte mais importante de qualquer aplicação Spring. O ApplicationContext do Spring é responsável por inicializar os Beans Spring definidos no arquivo de configuração de beans Spring.
Ciclo de Vida do Bean Spring
O Contexto Spring também é responsável por injetar dependências no bean, seja através de métodos setter ou construtores ou por autowiring do Spring. Às vezes, queremos inicializar recursos nas classes de bean, por exemplo, criar conexões de banco de dados ou validar serviços de terceiros no momento da inicialização antes de qualquer solicitação do cliente. O framework Spring fornece diferentes maneiras através das quais podemos fornecer métodos de pós-inicialização e pré-destruição em um ciclo de vida de bean Spring.
- Implementando as interfaces InitializingBean e DisposableBean – Ambas essas interfaces declaram um único método onde podemos inicializar/fechar recursos no bean. Para pós-inicialização, podemos implementar a interface
InitializingBean
e fornecer a implementação do métodoafterPropertiesSet()
. Para pré-destruição, podemos implementar a interfaceDisposableBean
e fornecer a implementação do métododestroy()
. Esses métodos são os métodos de retorno de chamada e são semelhantes às implementações de ouvintes de servlet. Esta abordagem é simples de usar, mas não é recomendada, pois criará um acoplamento forte com o framework Spring em nossas implementações de bean. - Fornecendo valores de atributos init-method e destroy-method para o bean no arquivo de configuração de bean do Spring. Esta é a abordagem recomendada devido à ausência de dependência direta ao framework Spring, e podemos criar nossos próprios métodos.
Observe que tanto os métodos post-init quanto pre-destroy devem não ter argumentos, mas podem lançar exceções. Também precisaríamos obter a instância do bean do contexto de aplicativo do Spring para a invocação desses métodos.
Ciclo de Vida do Bean Spring – @PostConstruct, @PreDestroy Anotações
O framework Spring também suporta as anotações @PostConstruct
e @PreDestroy
para definir métodos pós-inicialização e pré-destruição. Essas anotações fazem parte do pacote javax.annotation
. No entanto, para que essas anotações funcionem, precisamos configurar nossa aplicação Spring para procurar por elas. Podemos fazer isso tanto definindo um bean do tipo org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
quanto usando o elemento context:annotation-config
no arquivo de configuração de beans Spring. Vamos escrever uma aplicação Spring simples para demonstrar o uso das configurações acima para o gerenciamento do ciclo de vida do bean Spring. Crie um projeto Maven Spring no Spring Tool Suite, e o projeto final terá a aparência da imagem abaixo.
Ciclo de Vida do Bean Spring – Dependências Maven
Não precisamos incluir dependências extras para configurar os métodos do ciclo de vida do bean Spring, nosso arquivo pom.xml é como qualquer outro projeto Maven Spring padrão.
<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>
Ciclo de Vida do Bean Spring – Classe de Modelo
Vamos criar uma classe Java Bean simples que será usada em classes de serviço.
package com.journaldev.spring.bean;
public class Employee {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Ciclo de Vida do Bean do Spring – InitializingBean, DisposableBean
Vamos criar uma classe de serviço onde implementaremos ambas as interfaces para métodos pós-inicialização e pré-destruição.
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");
}
}
}
Ciclo de Vida do Bean do Spring – pós-inicialização personalizada, pré-destruição
Como não queremos que nossos serviços tenham dependência direta do framework Spring, vamos criar outra forma de classe de Serviço de Funcionário onde teremos métodos do ciclo de vida do Spring pós-inicialização e pré-destruição, e os configuraremos no arquivo de configuração de bean do Spring.
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");
}
//método pré-destruição
public void destroy() throws Exception {
System.out.println("MyEmployeeService Closing resources");
}
//método pós-inicialização
public void init() throws Exception {
System.out.println("MyEmployeeService initializing to dummy value");
if(employee.getName() == null){
employee.setName("Pankaj");
}
}
}
Vamos dar uma olhada no arquivo de configuração de bean do Spring em um instante. Antes disso, vamos criar outra classe de serviço que usará as anotações @PostConstruct e @PreDestroy.
Ciclo de Vida do Spring Bean – @PostConstruct, @PreDestroy
Abaixo está uma classe simples que será configurada como um bean Spring, e para os métodos pós-inicialização e pré-destruição, estamos usando as anotações @PostConstruct e @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 destory(){
System.out.println("MyService destroy method called");
}
}
Ciclo de Vida do Spring Bean – Arquivo de Configuração
Vamos ver como configuraremos nossos beans no arquivo de contexto do Spring.
<?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>
Observe que não estou inicializando o nome do funcionário na definição do bean. Como o EmployeeService está usando interfaces, não precisamos de nenhuma configuração especial aqui. Para o bean MyEmployeeService, estamos usando os atributos init-method e destroy-method para informar ao framework Spring nossos métodos personalizados a serem executados. A configuração do bean MyService não tem nada de especial, mas, como você pode ver, estou habilitando a configuração baseada em anotações para isso. Nosso aplicativo está pronto, vamos escrever um programa de teste para ver como diferentes métodos são executados.
Ciclo de Vida do Bean Spring – Programa de Teste
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");
}
}
Ao executar o programa de teste acima, obtemos a seguinte saída:
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
Pontos Importantes do Ciclo de Vida do Bean Spring:
- A partir da saída do console, fica claro que o Contexto do Spring primeiro utiliza o construtor sem argumentos para inicializar o objeto do bean e em seguida chama o método pós-inicialização.
- A ordem de inicialização do bean é a mesma que está definida no arquivo de configuração do bean do Spring.
- O contexto é retornado somente quando todos os beans do Spring são inicializados corretamente com as execuções do método pós-inicialização.
- O nome do funcionário é impresso como “Pankaj” porque foi inicializado no método pós-inicialização.
- Ao fechar o contexto, os beans são destruídos na ordem inversa à qual foram inicializados, ou seja, em ordem LIFO (Last-In-First-Out).
Você pode descomentar o código para obter o bean do tipo MyEmployeeService
e confirmar que a saída será semelhante e seguirá todos os pontos mencionados acima.
Interfaces Spring Aware
Às vezes, precisamos de objetos do Spring Framework em nossos beans para realizar algumas operações, como ler parâmetros ServletConfig e ServletContext ou saber as definições de bean carregadas pelo ApplicationContext. É por isso que o Spring Framework fornece um conjunto de interfaces *Aware que podemos implementar em nossas classes de bean. org.springframework.beans.factory.Aware
é a interface de marcação raiz para todas essas interfaces *Aware. Todas as interfaces *Aware são subinterfaces de Aware e declaram um único método setter a ser implementado pelo bean. Em seguida, o contexto Spring usa injeção de dependência baseada em setter para injetar os objetos correspondentes no bean e torná-los disponíveis para nosso uso. As interfaces Aware do Spring são semelhantes aos ouvintes de servlet com métodos de retorno de chamada e implementam o padrão de design observador. Algumas das interfaces Aware importantes são:
- ApplicationContextAware – para injetar o objeto ApplicationContext, exemplo de uso é obter a matriz de nomes de definição de bean.
- BeanFactoryAware – para injetar o objeto BeanFactory, exemplo de uso é verificar o escopo de um bean.
- BeanNameAware – para saber o nome do bean definido no arquivo de configuração.
- ResourceLoaderAware – para injetar o objeto ResourceLoader, exemplo de uso é obter o fluxo de entrada para um arquivo no classpath.
- ServletContextAware – para injetar o objeto ServletContext em uma aplicação MVC, exemplo de uso é ler parâmetros e atributos de contexto.
- ServletConfigAware – para injetar o objeto ServletConfig em uma aplicação MVC, exemplo de uso é obter parâmetros de configuração do servlet.
Vamos ver o uso dessas interfaces Aware em ação implementando algumas delas em uma classe que configuraremos como um bean Spring.
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");
}
}
Exemplo de Arquivo de Configuração Spring *Aware
Arquivo de configuração de bean Spring muito simples.
<?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>
Programa de Teste Spring *Aware
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();
}
}
Agora, ao executar a classe acima, obtemos a seguinte saída.
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
A saída do console do programa de teste é fácil de entender, não entrarei em muitos detalhes sobre isso. Isso é tudo para os métodos do ciclo de vida do Spring Bean e para a injeção de objetos específicos do framework nos beans Spring. Por favor, faça o download do projeto de exemplo no link abaixo e analise-o para aprender mais sobre eles.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-life-cycle