Desenvolvimento - Java

Migrando Spring IOC com XML para IOC com anotação

Veja nesse artigo a migração de um sistema utilizando a inversão controle com Spring XML sendo migrada para a forma com anotações. O Spring permite duas formas de injeção de dependência, através de XML e anotação. Foi criado um sistema veicular para demonstrarmos isso.

por Diego Morais Leite



Veja nesse artigo a migração de um sistema utilizando a inversão controle com Spring XML sendo migrada para a forma com anotações. O Spring permite duas formas de injeção de dependência, através de XML e anotação. Foi criado um sistema veicular para demonstrarmos isso.

Ferramentas utilizadas:

  • IDE Eclipse Kepler
  • Java 6
  • Spring Framework
  • Apache Commons Logging

O download do framework pode ser feito no endereço: http://www.springsource.org/download/community

Para esse artigo foi utilizado a versão do Spring 3.2.3. Foi feito o download do arquivo spring-framework-3.2.3.RELEASE-dist.zip. Ao descompactar o arquivo, a pasta terá a seguinte estrutura:

Estrutura de pastas

Figura 1: Estrutura de pastas

Os jars do Spring estão localizados na pasta libs.

Veja que há vários jars nesta pasta. Nas versões recentes do Spring, o Spring Source separou as diversas comunidades que compõem o framework Spring, para que o desenvolvedor escolha somente o que lhe interessar, no nosso caso será apenas a injeção de dependência.

Observação: Nas versões anteriores do Spring, todos esses jars eram agrupados em apenas um jar.

Para o nosso projeto será necessário baixar o framework commons-logging da Apache. O Spring possui dependência desse framework. Este download pode ser feito no endereço: http://commons.apache.org/proper/commons-logging/download_logging.cgi

Os jars utilizados nesse artigo para o projeto Veicular, foram:

Jars da aplicação

Figura 2: Jars da aplicação

Inversão de Controle com XML

Foi criado o projeto SpringProject no eclipse utilizando o Java 6. Primeiramente, o projeto utilizará a forma de XML para controle dos beans, ou seja, os beans serão declarados em arquivos de XML.

Assim o container do Spring registrará todos os beans e componentes descritos nesses arquivos.

Abaixo, a estrutura do projeto criado:

Estrutura do projeto com XML

Figura 3: Estrutura do projeto com XML

Os jars utilizados, descritos acima, devem ser adicionados no Java Build Path do projeto. Qualque dúvida, acesse a url: http://www.wikihow.com/Add-JARs-to-Project-Build-Paths-in-Eclipse-%28Java%29

Os fontes do projeto estão disponíveis para download no final desse artigo. Mas segue abaixo, para simples conferência:

Listagem 1: Classe Main

package org.project.spring;

import java.util.List;

import org.project.spring.entity.Carro;
import org.project.spring.factory.SingletonBeanFactory;
import org.project.spring.service.VeiculoService;
import org.springframework.beans.factory.BeanFactory;

public class Main {

	public static void main(String args[]){
		
		final BeanFactory beanFactory = SingletonBeanFactory.getBeanFactory();
	 
		final VeiculoService veiculoService = (VeiculoService)beanFactory.getBean("veiculoService");
		
		final List<Carro> listaCarro = veiculoService.listarVeiculoPorMontadora( "Fiat" );
		
		for(Carro carro : listaCarro){
			System.out.println(carro.toString());
		}
	}
}

Listagem 2: Classe VeiculoDAO

package org.project.spring.dao;

import java.util.ArrayList;
import java.util.List;

import org.project.spring.entity.Carro;

public class VeiculoDAO {

	private String qtdMaxima;
	
	public VeiculoDAO(String qtdMaxima){
		this.qtdMaxima = qtdMaxima;
	}
	
	public List<Carro> findVeiculo(String montadora){
		
		final List<Carro> listaCarro = new ArrayList<Carro>();
		
		int maximo = Integer.parseInt(this.qtdMaxima);
		
		for( int contador = 0 ; contador < maximo ; contador++ ){
			listaCarro.add(new Carro(contador, "Punto", montadora));
		}
		
		return listaCarro;
	}
	
	
}

Listagem 3: Classe Carro

package org.project.spring.entity;

public class Carro {

	private int id;
	private String nome;
	private String montadora;
	
	public Carro(int id, String nome, String montadora){
		this.id = id;
		this.nome = nome;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}
	
	public String getMontadora() {
		return montadora;
	}

	public void setMontadora(String montadora) {
		this.montadora = montadora;
	}
	
	@Override
	public String toString(){
		return "Id: "+this.id+" - Nome: "+this.nome;
	}
}

Listagem 5: Classe SingletonBeanFactory

package org.project.spring.factory;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SingletonBeanFactory {

    private static ApplicationContext applicationContext ;
    
    private SingletonBeanFactory() {
        /* Design pattern singleton */
    }
    public static BeanFactory getBeanFactory() {
        if (applicationContext == null) {
        	applicationContext = new ClassPathXmlApplicationContext(new String[] {"org/project/spring/resource/spring/application-context.xml"});
        }

        return applicationContext;
    }

}

Listagem 6: Classe VeiculoService

package org.project.spring.service;

import java.util.List;

import org.project.spring.dao.VeiculoDAO;
import org.project.spring.entity.Carro;

public class VeiculoService {

	private VeiculoDAO dao;
	
	public List<Carro> listarVeiculoPorMontadora(String montadora){
		return dao.findVeiculo(montadora);
	}

	public VeiculoDAO getDao() {
		return dao;
	}

	public void setDao(VeiculoDAO dao) {
		this.dao = dao;
	}
	
}

Arquivo de Propriedade: veiculo.properties

veiculo.quantidade.maxima=5

Listagem 7: Arquivo de configuração geral do Spring application-context.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" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
	
	<context:property-placeholder location="classpath:org/project/spring/resource/veiculo.properties"/>
	
	<import resource="repository.xml" />
	<import resource="service.xml" />
	
</beans>

Listagem 8: Arquivo de configuração de repositórios do Spring repository.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-3.2.xsd">
 
	<bean id="veiculoDAO" class="org.project.spring.dao.VeiculoDAO" >
		<constructor-arg index="0" value="${veiculo.quantidade.maxima}" />
	</bean>
 
</beans>

Listagem 9: Arquivo de configuração de serviços do Spring service.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-3.2.xsd">
 
	<bean id="veiculoService" class="org.project.spring.service.VeiculoService" >
		<property name="dao" ref="veiculoDAO" />
	</bean>
 
</beans>

Veja que a classe SingletonBeanFactory é um singleton responsável por obter referência ao container do Spring. Essa prática foi utilizada para que nessa aplicação, tenha apenas um container de beans.

Nesse singleton, foi utilizado a classe ClassPathXmlApplicationContext passando como parâmetro o caminho do arquivo XML geral. Essa classe do Spring é responsável por criar e gerenciar o container dos beans.

Outra característica desse aplicativo, foi a injeção de arquivo de propriedades(properties) em atributos de beans, como exemplo, a classe VeiculoDAO que teve o seu atributo qtdMaxima injetado com o valor da chave “veiculo.quantidade.maxima” do arquivo veiculo.properties.

Ao executar o arquivo Main.java, você obterá a seguinte saída:

Id: 0 - Nome: Punto
Id: 1 - Nome: Punto
Id: 2 - Nome: Punto
Id: 3 - Nome: Punto
Id: 4 - Nome: Punto

Inversão de Controle com Anotação

Trabalhando com o mesmo projeto SpringProject, foram feitas algumas alterações.

Abaixo, a nova estrutura do projeto:

Estrutura do projeto com anotação

Figura 4: Estrutura do projeto com anotação

O arquivo application-context.xml foi alterado e adicionado a tag componente-scan. Segue:

<?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-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
	
	<context:property-placeholder location="classpath:org/project/spring/resource/veiculo.properties"/>
	
	<context:component-scan base-package="org.project.spring.dao,org.project.spring.service,org.project.spring.resource" />
	
</beans>

Veja que não é necessário mais importar os arquivos repository.xml e service.xml. O tag componente-scan faz com que o próprio framework detecte e instancie os beans, escaneando as classes dos packages definidos no atributo location. Dessa forma, não é mais necessário o desenvolvedor declarar manualmente os beans em arquivos XML.

O Spring escaneará e processará as anotações abaixo:

  • @Component - Indica o component para beans em geral
  • @Repository - Indica o DAO component para camada de persistência
  • @Service - Indica o Service component para a camada de negócio.
  • @Controller - Indica o Controller component para a camada de apresentação.

As principais camadas da aplicação possuem uma anotação, como a camada de serviço, persistência e apresentação. É uma boa prática a utilização dessas anotações para cada camada. Para as camadas em geral e outros beans, pode-se utilizar a anotação @Component.

No final, todas as anotações transformarão as classes anotadas em componentes do container Spring.

As classes VeiculoService e VeiculoDAO foram alteradas para receber a anotação.

Listagem 10: Classe VeiculoService

package org.project.spring.service;

import java.util.List;

import org.project.spring.dao.VeiculoDAO;
import org.project.spring.entity.Carro;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class VeiculoService {

	@Autowired
	private VeiculoDAO dao;
	
	public List<Carro> listarVeiculoPorMontadora(String montadora){
		return dao.findVeiculo(montadora);
	}

	public VeiculoDAO getDao() {
		return dao;
	}

	public void setDao(VeiculoDAO dao) {
		this.dao = dao;
	}
	
}

A anotação @Autowired atribuiu ao atributo dao da classe VeiculoService a referência do bean VeiculoDAO. Não foi necessário passar o nome do bean na anotação, pois ela própria pega o nome da classe para procurar no container.

Listagem 11: Classe VeiculoDAO

package org.project.spring.dao;

import java.util.ArrayList;
import java.util.List;

import org.project.spring.entity.Carro;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

@Repository
public class VeiculoDAO {

	@Value( "${veiculo.quantidade.maxima}" )
	private String qtdMaxima;
	
	
	public List<Carro> findVeiculo(String montadora){
		
		final List<Carro> listaCarro = new ArrayList<Carro>();
		
		int maximo = Integer.parseInt(this.qtdMaxima);
		
		for( int contador = 0 ; contador < maximo ; contador++ ){
			listaCarro.add(new Carro(contador, "Punto", montadora));
		}
		
		return listaCarro;
	}
	
}

Veja que para atribuir o valor de chaves de arquivos de propriedade ao atributo qtdMaximo, foi utilizado a anotação @Value passando como parâmetro "${veiculo.quantidade.maxima}", em que possui o nome da chave da propriedade.

Conclusão

Vimos como é simples a migração da Injeção de dependência da forma XML para a forma de anotação. O grande segredo é o tag component-scan.

Bom pessoal, até a próxima!

Diego Morais Leite

Diego Morais Leite - Arquiteto de Sistemas Sênior. Formado em Processamento de Dados pela Faculdade de Tecnologia de Sorocaba. Engenheiro de Softwares pela Universidade Estadual de Campinas(UNICAMP). Desenvolve com a plataforma Java mais de 6 anos. Possui as certificações SCJP, SCWCD e SCBCD. Mestrando em Ciência da Computação pela UNICAMP. Ministra treinamentos para certificação Java e é Scrum Master de projetos ágeis. Trabalhou em projetos nas áreas de saúde, seguros, finanças e de controle de tráfego do espaço áereo brasileiro. Atualmente, presta serviços para financeiras.