Ciclo de Vida do Bean Spring

Hoje vamos analisar o Ciclo de Vida do Spring Bean. Os Spring Beans são a parte mais importante de qualquer aplicação Spring. O ApplicationContext do Spring é responsável por inicializar os Spring Beans definidos no arquivo de configuração de beans do Spring.

Ciclo de Vida do Spring Bean

O Contexto do Spring também é responsável por realizar injeções de dependência no bean, seja através de métodos setter ou construtor, ou por meio do autowiring do Spring. Às vezes, queremos inicializar recursos nas classes de bean, como 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 pelas quais podemos fornecer métodos de pós-inicialização e pré-destruição em um ciclo de vida do bean do Spring.

  1. Ao implementar 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étodo afterPropertiesSet(). Para pré-destruição, podemos implementar a interface DisposableBean e fornecer a implementação do método destroy(). Esses métodos são métodos de retorno de chamada e semelhantes às implementações de ouvintes de servlet. Esta abordagem é simples de usar, mas não é recomendada porque criará um acoplamento forte com o framework Spring em nossas implementações de bean.
  2. Fornecendo valores de atributos init-method e destroy-method para o bean no arquivo de configuração de bean 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 ambos os métodos post-init e pré-destruição devem ter argumentos, mas podem lançar exceções. Também precisaremos obter a instância do bean do contexto da aplicação Spring para a invocação desses métodos.

Ciclo de Vida do Bean Spring – @PostConstruct, @PreDestroy Annotations

O framework Spring também suporta as anotações @PostConstruct e @PreDestroy para definir métodos de 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 as anotações. Podemos fazer isso definindo um bean do tipo org.springframework.context.annotation.CommonAnnotationBeanPostProcessor ou pelo elemento context:annotation-config no arquivo de configuração do bean Spring. Vamos escrever uma aplicação Spring simples para mostrar 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 de 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 do Modelo

Vamos criar uma classe Java bean simples que será utilizada 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 Spring – InitializingBean, DisposableBean

Vamos criar uma classe de serviço onde implementaremos ambas as interfaces para os métodos de 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 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 de pós-inicialização e pré-destruição, e os configuraremos no arquivo de configuração do bean 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 examinar o arquivo de configuração do bean Spring em breve. Antes disso, vamos criar outra classe de serviço que utilizará as anotações @PostConstruct e @PreDestroy.

Ciclo de Vida do Bean Spring – @PostConstruct, @PreDestroy

Abaixo está uma classe simples que será configurada como 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 Bean Spring – Arquivo de Configuração

Vamos ver como vamos configurar nossos beans no arquivo de contexto 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 de seu 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. Nossa aplicação está pronta, vamos escrever um programa de teste para ver como os 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 executarmos 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:

  • Pela saída no console, fica claro que o Contexto Spring primeiro usa o construtor sem argumentos para inicializar o objeto do bean e depois 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 Spring.
  • O contexto só é retornado quando todos os beans Spring são inicializados corretamente com a execução 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, na 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 Conscientes do Spring

Às vezes, precisamos de objetos do Framework Spring em nossos beans para realizar algumas operações, como por exemplo, ler parâmetros ServletConfig e ServletContext ou saber as definições de beans carregadas pelo ApplicationContext. É por isso que o framework Spring fornece um conjunto de interfaces *Aware que podemos implementar em nossas classes de bean. `org.springframework.beans.factory.Aware` é a interface marcadora 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. Então, o contexto do Spring usa injeção de dependência baseada em setter para injetar os objetos correspondentes no bean e torná-lo disponível para nosso uso. As interfaces Aware do Spring são similares aos ouvintes de servlet com métodos de retorno de chamada e implementação do padrão de design observer. Algumas das interfaces Aware importantes são:servlet listeners com métodos de retorno de chamada e implementação do padrão de design observer. Algumas das interfaces Aware importantes são:

  • ApplicationContextAware – para injetar o objeto ApplicationContext, exemplo de uso é obter o array de nomes de definição de beans.
  • 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 bean do 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 do 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 executarmos 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 é simples de entender, não entrarei em muitos detalhes sobre isso. Isso é tudo para os métodos do ciclo de vida do bean do Spring e a injeção de objetos específicos do framework nos beans do Spring. Por favor, baixe o projeto de exemplo no link abaixo e analise-o para aprender mais sobre eles.

Baixar Projeto Ciclo de Vida do Spring Bean

Source:
https://www.digitalocean.com/community/tutorials/spring-bean-life-cycle