Contêiner IoC, Injeção de Dependência no Spring Framework: Configuração via XML, Anotações e Java Config

Conceitos Fundamentais

  • IoC (Inversão de Controle): Transferência do controle da criação e gerenciamento de objetos para um terceiro (contêiner Spring)
  • DI (Injeção de Dependência): Técnica que utiliza reflexão para implementar IoC, onde as dependências são injetadas nos objetos
  • Contêiner IoC do Spring: Componente responsável por criar e gerenciar as dependências entre os objetos
  • Gerenciamento de objetos via XML: Utilização de applicationContext.xml para configuração

Utilizando o Spring Framework - Configuração de Contêiner IoC via XML

1. Importando as dependências do Spring

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.3.18</version>
</dependency>

2. Criando o arquivo de configuração applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

3. Instanciando Objetos

O gerenciamento de beans via XML pode ser feito de várias formas:

Criação de objetos via construtor

<!-- Utilizando a tag constructor-arg para criar objetos através do construtor com parâmetros -->
<bean id="bananaPremium" class="br.exemplo.spring.ioc.entity.Banana">
    <constructor-arg name="nome" value="Prata"></constructor-arg>
    <constructor-arg name="cor" value="Amarela"></constructor-arg>
    <constructor-arg name="origem" value="Brasil"></constructor-arg>
</bean>

Criação de objetos via fábrica estática (pouco utilizado)

<!-- Criação de objetos através de fábrica estática -->
<bean id="bananaDoce" class="br.exemplo.spring.ioc.factory.BananaFactory" factory-method="criarBananaDoce"></bean>

package br.exemplo.spring.ioc.factory;

import br.exemplo.spring.ioc.entity.Banana;

public class BananaFactory {
    public static Banana criarBananaDoce() {
        Banana bananaDoce = new Banana();
        bananaDoce.setNome("Caturra");
        bananaDoce.setCor("Amarela");
        bananaDoce.setOrigem("Colômbia");
        return bananaDoce;
    }
}

Criação de objetos via instância de fábrica (pouco utilizado)

O contêiner IoC instancia a classe fábrica e chama o método de instância correspondente para criar o objeto:

<!-- Criação de objetos através de instância de fábrica -->
<bean id="instanciaFabrica" class="br.exemplo.spring.ioc.factory.BananaFactoryInstance"></bean>
<bean id="bananaAcida" factory-bean="instanciaFabrica" factory-method="criarBananaAcida"></bean>

package br.exemplo.spring.ioc.factory;

import br.exemplo.spring.ioc.entity.Banana;

public class BananaFactoryInstance {
    public Banana criarBananaAcida() {
        Banana bananaAcida = new Banana();
        bananaAcida.setNome("Ouro");
        bananaAcida.setCor("Verde");
        bananaAcida.setOrigem("África");
        return bananaAcida;
    }
}

4. Inicializando o Contêiner IoC

Inicialização do contêiner IoC e instanciação dos objetos conforme o arquivo de configuração XML:

public static void main(String[] args) {
    ApplicationContext contexto = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}

5. Recuperando Beans do Contêiner IoC

Banana bananaDoce = contexto.getBean("bananaDoce", Banana.class);

6. Injeção de Dependências

Injeção de dependências via setter para valores estáticos

Utilização da tag property para injeção de dependências via setter. A classe deve implementar métodos getter e setter:

<bean id="bananaDoce" class="br.exemplo.spring.ioc.entity.Banana">
    <property name="nome" value="Caturra"></property>
    <property name="cor" value="Amarela"></property>
    <property name="origem" value="Colômbia"></property>
</bean>

Injeção de objetos via setter

Utilização da tag ref para injeção de objetos dependentes:

<bean id="joao" class="br.exemplo.spring.ioc.entity.Pessoa">
    <property name="nome" value="João"></property>
    <property name="fruta" ref="bananaDoce"></property>
</bean>

Injeção de dependências via construtor

<bean id="maria" class="br.exemplo.spring.ioc.entity.Pessoa">
    <constructor-arg name="nome" value="Maria"></constructor-arg>
    <constructor-arg name="fruta" ref="bananaPremium"></constructor-arg>
</bean>

Injeção de coleções

  • List: Por padrão utiliza ArrayList
  • Set: Por padrão utiliza LinkedHashSet (mantém ordem)
  • Map: Por padrão utiliza LinkedHashMap (mantém ordem)
  • Properties: Armazena dados como pares chave-valer, onde chave e valor devem ser strings

Recuperando todos os beans do contêiner

String[] nomesBeans = contexto.getBeanDefinitionNames();
for (String nomeBean : nomesBeans) {
    System.out.println(nomeBean);
    System.out.println(contexto.getBean(nomeBean));
}

Atributo scope do bean

<bean id="..." class="..." scope="prototype">

  • singleton: Instância única no contêiner IoC (padrão). Pode causar problemas em ambientes multithread.
  • prototype: Múltiplas instâncias, consome mais recursos, sem problemas de thread safety.

Ciclo de vida do bean

Os atributos init-method e destroy-method são subcampos do bean que garantem a execução de métodos específicos durante o ciclo de vida do bean.

Utilizando o Spring Framework - Configuração de Contêiner IoC via Anotações

Quatro tipos de anotações de componentes

  • As anotações de tipo de componente definem por padrão o bean id como o nome da classe com a primeira letra minúscula.
  • Para classes que não se enquadram em categorias específicas, utiliza-se a anotação Component.

Inicializando o contêiner com anotações

O contêiner IoC, durante a inicialização, escaneia automaticamente as quatro anotações de tipo de componente e instancia os objetos correspondentes.

Configuração XML para ativar o scan de componentes

Em relação à configuração XML do contêiner IoC, é necessário adicionar uma linha adicional para o namespace do contexto:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="br.exemplo">
</beans>

Dois tipos de anotações de montagem automática

  • Se a anotação de montagem for colocada no método setter, a injeção é feita automaticamente por tipo/nome no parâmetro do método.
  • Se colocada no atributo, o contêiner IoC utiliza reflexão para modificar temporariamente o modificador do atributo para public, realizar a atribuição e depois retornar ao modificador original. O método setter não é executado.
  • Não é recomendado usar injeção por tipo, pois ao montar uma interface por tipo, todas as suas implementações seriam beans do mesmo tipo, causando conflitos.

@Resource

  1. Se o atributo name estiver definido, o bean correspondente é injetado diretamente no contêiner IoC com base no name.
  2. Se o atributo name não estiver definido:
    1. Primeiro, tenta corresponder o nome do atributo como bean name no contêiner IoC. Se encontrar, injeta.
    2. Se não encontrar, corresponde por tipo (similar ao @Autowired). Em caso de conflito, usa a anotação @Primary para resolver.

Recomendação: Definir o atributo name || Garantir que o nome do atributo corresponda ao bean name.

Anotações de metadados

Notificam o contêiner IoC para carregar arquivos de propriedades durante a inicialização:

<context:property-placeholder location="classpath:config.properties"></context:property-placeholder>

@Value

  1. Criar um novo arquivo de configuração (config.properties)
  2. Adicionar a anotação @Value

Utilizando o Spring Framework - Configuração de Contêiner IoC via Java Config

Utilização de uma classe Java independente para geranciar objetos e dependências.

Anotações principais do Java Config

Instanciação de objetos via Java Config

  1. Adicionar uma classe de configuração Config.java
    • Adicionar a anotação @Configuration, indicando que a classe atual é uma classe de configuração, substituindo o arquivo applicationContext.xml
    • Adicionar a anotação @Bean, onde o Java Config utiliza métodos para criar objetos, colocando o objeto retornado pelo método no contêiner. O bean id é igual ao nome do método.
@Configuration
public class Config {
    @Bean
    public Banana bananaDoce() {
        return new Banana();
    }
}

Ponto de entrada da aplicação

AnnotationConfigApplicationContext contexto = new AnnotationConfigApplicationContext(Config.class);

Injeção de dependências via Java Config

O objeto dependente é passado como parâmetro no método. O nome do objeto é usado como bean id no contêiner. Se existir, corresponde diretamente; caso contrário, realiza correspondência por tipo.

@Configuration
public class Config {
    @Bean
    public Banana bananaDoce() {
        return new Banana();
    }

    @Bean
    public Pessoa joao(Banana bananaDoce) {
        Pessoa pessoa = new Pessoa();
        pessoa.setFruta(bananaDoce);
        return pessoa;
    }
}

Integração Spring com JUnit4

1. Adicionar dependências do spring-test e junit

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>5.3.18</version>
</dependency>

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>

2. Configurar a classe de teste

Adicionar as anotações @RunWith e @ContextConfiguration na classe de teste, transferindo o controle de execução do JUnit4 para o Spring Test, que inicializa automaticamente o contêiner IoC antes da execução dos casos de teste.

3. Adicionar propriedades a serem testadas

4. Escrever métodos de teste

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringTest {
    @Resource
    private Pessoa joao;

    @Test
    public void testJoao() {
        joao.comer();
    }
}

Tags: Spring Framework IoC Injeção de Dependência XML Anotações

Publicado em 5-30 10:57 por Thomas