Explorando @Bean — Spring IoC

Décio Heinzelmann Luckow
5 min readOct 28, 2017

--

No Spring, os objetos que formam a espinha dorsal da sua aplicação e que sejam gerenciados pelo Spring são chamados de beans. Um bean é um objeto que é instanciado, montado e gerenciado pelo Spring IoC container.

O Spring IoC container busca informações em XML, annotations ou código java sobre como os beans devem ser instanciados, configurados e montados, e como se relacionam com outros beans. A resolução do relacionamento entre beans é conhecida como injeção de dependências. Se você cria uma classe que depende de alguns bean, só precisa se preocupar com o que a sua classe depende, e não com o que suas dependências dependem.

Existem diversas forma de se criar beans no Spring, você pode criar classes anotadas com @Component, @Configuration ou @Service para serem gerenciadas pelo Spring. Assim como pode usar o @Bean em um método, e tornar a instância retornada pelo método como um objeto gerenciado pelo Spring (seja de uma classe própria ou de terceiros).

Estas classes, que na visão do Spring são os beans, para você nada mais são do que classes você você irá escrever as regras de funcionamento da sua aplicação.

Neste artigo vou explorar a criação de beans usando a annotation @Bean e explorar diversas forma de configuração e injeção desta.

Por padrão, beans que usam @Bean são criados dentro de classes do tipo @Configuration, conforme o exemplo a seguir:

Também é possível criar em classes do tipo @Component, porém neste caso, perde-se alguns recursos (que serão explicados adiante)

Neste caso, estamos criando dois beans do tipo ClientService. O nome do bean no contexto do Spring será exatamente o nome do método, ou seja: clientService1 e clientService2.

Se quisermos injetar estes beans em outra classe, podemos fazer da seguinte maneira:

Veja que, por mais que sejam dois beans diferentes, eles estão declarados com a mesma interface ClientService. Neste caso será o nome da propriedade que irá diferenciar qual bean será injetado.

Se tentarmos injetar o serviço nomeando a propriedade como clientService10, vamos obter o seguinte erro, alertando que um bean com o nome clientService10 não foi encontrado.

***************************
APPLICATION FAILED TO START
***************************
Description:
Field clientService10 in br.com.decioluckow.springexamples.SpringExamplesApplication required a single bean

Isso significa que só é possível injetar um bean com uma propriedade de mesmo nome? Não… se você quiser nomear a propriedade do bean de clientServiceDois, por exemplo, poderá usar um @Qualifier para identificar qual bean deve ser injeado, exemplo:

@Autowired
@Qualifier(“clientService2”)
private ClientService clientServiceDois;

Comportamento exclusivo de @Configuration

Vamos voltar na classe ApplicationConfig para explorar o bean clientDao:

Repare que os dois beans clientService chamam o método clientDao(), cada um deles, no momento de montar sua instância, realiza um setClientDao(clientDao()).

Naturalmente podemos pensar que está sendo criada uma nova instância de ClientDaoImpl para cada chamada… mas não na classe @Configuration.

Se isto fosse feito em uma classe @Component seria criada uma instância para cada chamada.

A mágica acontece somente em classes @Configuration graças ao CGLIB, que no startup da aplicação cria uma extensão da classe ApplicationConfig. Esta classe filha, verifica se já existe uma instância do bean no contexto do Spring a cada chamada do método clientDao(), e só chama o método na classe pai (a que você programou) se não existir.

Para que o CGLIB consiga implementar este comportamento é necessário que a classe anotada com @Configuration não seja final.

Dependência como parâmetro

Também é possível declarar as dependência para a construção de um @Bean por meio dos parâmetros do método, conforme o exemplo:

Neste caso o comportamento da injeção do parâmetro do método é um pouco diferente da injeção da propriedade na classe, como já vimos anteriormente.

Se existir somente um bean do tipo ClientDao, este será injetado independente do nome do parâmetro. Desta forma, poderíamos ter nomeado o parâmetro como clientDaoDois e ele também teria sido injetado.

Por outro lado, se existir mais de um bean do tipo ClientDao, o Spring irá exigir que você nomeie o parâmetro conforme o nome do bean, ou utilize o @Qualifier da mesma forma como já foi mostrado, exemplo:

@Bean
public ClientService clientService3(
@Qualifier(“clientDao”) ClientDao clientDaoDois)

Ciclo de vida do bean

Por “ciclo de vida” podemos entender como os momentos de criação e destruição de um bean.

A criação é o momento que o bean é instanciado e passar a ser gerenciado pelo Spring, resolvendo também suas dependências com outros beans. Já a destruição é quando o bean é removido do contexto do Spring, este momento geralmente acontece no shutdown da aplicação, ou quando é programaticamente removido pela própria aplicação.

A anotação @Bean permite definir métodos a serem chamado na criação e na destruição do bean, conforme o exemplo:

@Bean(initMethod = “init”, destroyMethod = “cleanup”)

O Spring irá acionar automaticamente métodos públicos close ou shutdown do seu bean, se eles existirem. Um exemplo bastante útil é se você usar um InputStream como bean, que naturalmente já possui um método close().

Se estes métodos close ou shutdown existirem na classe, mas não puderem ser chamados, você deve informar “” para o atributo, exemplo:

@Bean(destroyMethod = “”)

Outras formas de interceptar o ciclo de vida do bean tanto em classe fornecidas por um método @Bean, quanto em classes anotadas por @Service ou @Component é utilizando as anotações da JSR-250 ( Common Annotations for the JavaTM Platform). Esta especificação prevê as anotações: @PostConstruct e @PreDestroy. Os métodos que tiverem estas anotações serão respectivamente chamados na criação e destruição do bean.

Por fim, as interfaces InitializingBean e DisposableBean, se implementas pela classe do bean dão acesso aos métodos afterPropertiesSet() e destroy() respectivamente, tendo o mesmo comportamento de initMethod/destroyMethod ou @PostConstruct e @PreDestroy.

Com tantas opções para se interceptar estes momentos do ciclo de vida de um bean, é importante conhecer a ordem de execução destes, como segue:

  1. Métodos anotados com @PostConstruct
  2. Métodos afterPropertiesSet() definidos por InitializingBean
  3. Métodos de criação, definidos por @Bean(initMethod=?)

Para o momento da destruição dos beans, a seguinte ordem:

  1. Métodos anotados com @PreDestroy
  2. Método destroy() definidos por DisposableBean
  3. Métodos de destruição, definidos por @Bean(destroyMethod=?)

Teste da aplicação

Ao executar a aplicação, podemos observar o comportamento dos beans conforme os logs gerados:

Impressão dos logs
Logs impressos

Veja que conforme a identificação das instâncias, o clientDao para todos os clientService é o mesmo, e que clientServiceDois contém a mesma instância de clientService2.

Downloads

Os testes deste artigo foram feito usando Spring Boot e Lombok, você pode obter o projeto completo no github em :

https://github.com/decioluckow/spring-examples-bean/archive/master.zip

Para instalar o Lombok na sua IDE, acesse https://projectlombok.org/

--

--