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
- Se o atributo name estiver definido, o bean correspondente é injetado diretamente no contêiner IoC com base no name.
- Se o atributo name não estiver definido:
- Primeiro, tenta corresponder o nome do atributo como bean name no contêiner IoC. Se encontrar, injeta.
- 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
- Criar um novo arquivo de configuração (config.properties)
- 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
- 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();
}
}