15. Spring IoC
15.1. Introduction
Propomos explorar as possibilidades de configuração e integração do framework Spring (http://www.springframework.org), bem como definir e utilizar o conceito de IoC (Inversão de Controle), também conhecido como injeção de dependência (Dependency Injection)
Consideremos a aplicação de três camadas que acabamos de construir:
![]() |
Para atender às solicitações do usuário, o controlador [Application] precisa se comunicar com a camada [service]. Em nosso exemplo, essa camada era uma instância do tipo [DaoImpl]. O controlador [Application] obteve uma referência à camada [service] em seu método [init] (parágrafo 14.8.3):
- linha 6: a camada [dao] foi instanciada pela criação explícita de uma instância [DaoImpl]
- linha 9: a camada [service] foi instanciada pela criação explícita de uma instância [ServiceImpl]
Vale lembrar que as classes [DaoImpl] e [ServiceImpl] implementam interfaces, respectivamente as interfaces [IDao] e [IService]. Em versões futuras, a interface [IDao] será implementada por uma classe que gerencia uma lista de pessoas armazenada em um banco de dados. Vamos chamar essa classe de [DaoBD] para fins de exemplo. Substituir a implementação [DaoImpl] da camada [dao] pela implementação [DaoBD] exigirá uma recompilação da camada [web]. De fato, a linha 6 acima, que instancia a camada [dao] com um tipo [DaoImpl], agora deve instanciá-la com um tipo [DaoBD]. Nossa camada [web] depende, portanto, da camada [dao]. A linha 9 acima mostra que ela também depende da camada [service].
O Spring IoC nos permitirá criar uma aplicação de três camadas em que as camadas são independentes umas das outras, c.a.d, de modo que alterar uma não exige a alteração das outras. Isso proporciona grande flexibilidade na evolução da aplicação.
A arquitetura anterior evoluirá da seguinte maneira:
![]() |
Com o [Spring IoC], o controlador [Application] obterá a referência de que precisa na camada [service] da seguinte maneira:
- em seu método [init], ele solicitará à camada [Spring IoC] que lhe forneça uma referência na camada [service]
- O [Spring IoC], então, utilizará um arquivo de configuração XML que indica qual classe deve ser instanciada e como ela deve ser inicializada.
- [Spring IoC] retorna ao controlador [Application] a referência da camada [service] criada.
A vantagem dessa solução é que, a partir de agora, os nomes das classes que instanciam as diferentes camadas não estão mais codificados de forma rígida no método [init] do controlador, mas simplesmente constam em um arquivo de configuração. Alterar a implementação de uma camada implicará uma alteração nesse arquivo de configuração, mas não no controlador.
Vamos agora apresentar as possibilidades do [Spring IoC] por meio de exemplos.
15.2. Spring IoC na prática
15.2.1. Spring
[Spring IoC] faz parte de um projeto mais amplo disponível no endereço [http://www.springframework.org/] (maio de 2006):
![]() | ![]() |
![]() |
- [1]: O Spring utiliza diversas tecnologias de terceiros, aqui denominadas dépendances. É necessário baixar a versão com dépendances para evitar ter que baixar posteriormente as bibliotecas dessas ferramentas de terceiros.
- [2]: a estrutura de diretórios do arquivo compactado baixado
- [3]: a distribuição [Spring], c.a.d. Os arquivos .jar do próprio projeto Spring, sem suas dependências. A aparência do Spring ([IoC]) é garantida pelos arquivos [spring-core.jar, spring-beans.jar].
- [4,5]: os arquivos das ferramentas de terceiros
15.2.2. Projetos Eclipse dos exemplos
Vamos criar três exemplos que ilustram o uso do Spring IoC. Todos eles estarão no seguinte projeto do Eclipse:

O projeto [springioc-exemples] está configurado para que os arquivos-fonte e as classes compiladas fiquem na raiz da pasta do projeto:
![]() |
- [1]: a estrutura de pastas do projeto [Eclipse]
- [2]: os arquivos de configuração do Spring estão na raiz do projeto, portanto, na pasta Classpath do aplicativo
- [3]: as classes do exemplo 1
- [4]: as classes do exemplo 2
- [5]: as classes do exemplo 3
- [6]: as bibliotecas do projeto [spring-core.jar, spring-beans.jar] estarão na pasta [dist] da distribuição do Spring e [commons-logging.jar] na pasta [lib/jakarta-commons]. Esses três arquivos foram incluídos no Classpath do aplicativo.
15.2.3. Exemplo 1
Os elementos do exemplo 1 foram colocados no pacote [springioc01] do projeto:
![]()
A classe [Personne] é a seguinte:
A classe apresenta:
- linhas 6-7: dois campos privados `nom` e `age`
- linhas 23-38: os métodos de leitura (get) e gravação (set) desses dois campos
- linhas 10-12: um método toString para recuperar o valor do objeto [Personne] na forma de uma cadeia de caracteres
- linhas 15-21: um método `init` que será chamado pelo Spring na criação do objeto, e um método `close` que será chamado na destruição do objeto
Para instanciar objetos do tipo [Personne] usando o Spring, utilizaremos o seguinte arquivo [spring-config-01.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="personne1" class="istia.st.springioc01.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Simon" />
<property name="age" value="40" />
</bean>
<bean id="personne2" class="istia.st.springioc01.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Brigitte" />
<property name="age" value="20" />
</bean>
</beans>
- linhas 3, 12: a tag <beans> é a tag raiz dos arquivos de configuração do Spring. Dentro dessa tag, a tag <bean> serve para definir os diferentes objetos a serem criados.
- linhas 4-7: definição de um bean
- linha 4: o bean se chama [personne1] (atributo id) e é uma instância da classe [istia.st.springioc01.Personne] (atributo class). O método [init] da instância será chamado assim que ela for criada (atributo init-method) e o método [close] da instância será chamado antes de sua destruição (atributo destroy-method).
- linha 5: definem o valor a ser atribuído à propriedade [nom] (atributo name) da instância [Personne] criada. Para realizar essa inicialização, o Spring utilizará o método [setNom]. Portanto, é necessário que esse método exista. É o que ocorre neste caso.
- linha 6: o mesmo vale para a propriedade [age].
- linhas 8-11: definição análoga de um bean chamado [personne2]
A classe de teste [Main] é a seguinte:
Comentários:
- linha 10: para obter os beans definidos no arquivo [spring-config-01.xml], utilizamos um objeto do tipo [XmlBeanFactory], que permite instanciar os beans definidos em um arquivo XML. O arquivo [spring-config-01.xml] será colocado no [ClassPath] do aplicativo, c.a.d, em um dos diretórios pesquisados pela máquina virtual Java quando ela procura uma classe referenciada pelo aplicativo. O objeto [ClassPathResource] serve para procurar um recurso no [ClassPath] de um aplicativo, neste caso, o arquivo [spring-config-01.xml]. O objeto [bf] obtido (Bean Factory) permite obter a referência de um bean chamado “XX” por meio da instrução bf.getBean("XX").
- linha 12: solicita-se uma referência ao bean denominado [personne1] no arquivo [spring-config-01.xml].
- linha 13: exibe-se o valor do objeto [Personne] correspondente.
- linhas 15-16: faz-se o mesmo para o bean chamado [personne2].
- linhas 18-19: solicita-se novamente o bean chamado [personne2].
- linha 21: são removidos todos os beans [bf] e c.a.d, bem como aqueles criados a partir do arquivo [spring-config-01.xml].
A execução da classe [Main] produz os seguintes resultados:
Comentários:
- a linha 1 foi obtida pela execução da linha 12 de [Main]. A operação
forçou a criação do bean [personne1]. Como na definição do bean [personne1] havia sido escrito [init-method="init"], o método [init] do objeto [Personne] criado foi executado. A mensagem correspondente é exibida.
- linha 2: a linha 13 de [Main] exibiu o valor do objeto [Personne] criado.
- linhas 3-4: o mesmo fenômeno se repete para o bean denominado [personne2].
- linha 5: a operação das linhas 18-19 de [Main]
personne2 = (Personne) bf.getBean("personne2");
System.out.println("personne2=" + personne2.toString());
não resultou na criação de um novo objeto do tipo [Personne]. Se tivesse sido o caso, teríamos visto a exibição do método [init]. Esse é o princípio do singleton. Por padrão, o Spring cria apenas uma única instância dos beans do seu arquivo de configuração. Trata-se de um serviço de referências a objetos. Se for solicitada a referência de um objeto ainda não criado, ele o cria e retorna uma referência. Se o objeto já tiver sido criado, o Spring se limita a fornecer uma referência. Aqui, como [personne2] já havia sido criado, o Spring se limita a retornar uma referência.
- As exibições nas linhas 6 e 7 foram provocadas pela linha 21 de [Main], que solicita a destruição de todos os beans referenciados pelo objeto [XmlBeanFactory bf], ou seja, os beans [personne1, personne2]. Como esses dois beans possuem o atributo [destroy-method="close"], o método [close] de ambos os beans é executado e provoca a exibição das linhas 6-7.
Agora que já dominamos os fundamentos de uma configuração do Spring, nossas explicações serão um pouco mais rápidas daqui em diante.
15.2.4. Exemplo 2
Os elementos do exemplo 2 estão localizados no pacote [springioc02] do projeto:

O pacote [springioc02] é obtido inicialmente por meio de copiar/colar do pacote [springioc01]; em seguida, adiciona-se a classe [Voiture] e adapta-se a classe [Main] ao novo exemplo.
A classe [Voiture] é a seguinte:
A classe apresenta:
- linhas 5-7: três campos privados: tipo, marca e proprietário. Esses campos podem ser inicializados e lidos por métodos públicos get e set dos beans, nas linhas 26-48. Eles também podem ser inicializados por meio do construtor Carro(String, String, Pessoa), definido nas linhas 13-17. A classe também possui um construtor sem argumentos para atender à norma JavaBean.
- linhas 20-23: um método toString para recuperar o valor do objeto [Voiture] na forma de uma cadeia de caracteres
- linhas 51-57: um método `init` que será chamado pelo Spring logo após a criação do objeto, e um método `close` que será chamado na destruição do objeto
Para criar objetos do tipo [Voiture], utilizaremos o seguinte arquivo Spring [spring-config-02.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="personne1" class="istia.st.springioc02.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Simon" />
<property name="age" value="40" />
</bean>
<bean id="personne2" class="istia.st.springioc02.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Brigitte" />
<property name="age" value="20" />
</bean>
<bean id="voiture1" class="istia.st.springioc02.Voiture" init-method="init" destroy-method="close">
<constructor-arg index="0" value="Peugeot" />
<constructor-arg index="1" value="307" />
<constructor-arg index="2">
<ref local="personne2" />
</constructor-arg>
</bean>
</beans>
Esse arquivo adiciona aos beans definidos em [spring-config-01.xml] um bean com a chave “voiture1” do tipo [Voiture] (linhas 12-17). Para inicializar esse bean, poderíamos ter escrito:
Em vez de escolher esse método já apresentado no exemplo 1, optamos por utilizar aqui o construtor Carro(String, String, Pessoa) da classe.
- linha 12: definição do nome do bean, de sua classe, do método a ser executado após sua instanciação e do método a ser executado após sua remoção.
- linha 13: valor do primeiro parâmetro do construtor [Voiture(String, String, Personne)].
- linha 14: valor do segundo parâmetro do construtor [Voiture(String, String, Personne)].
- linhas 15-17: valor do terceiro parâmetro do construtor [Voiture(String, String, Personne)]. Esse parâmetro é do tipo [Personne]. Como valor, é fornecida a referência (tag ref) do bean [personne2] definido no mesmo arquivo (atributo local).
Para nossos testes, utilizaremos a seguinte classe [Main]:
O método [main] solicita a referência do bean [voiture1] (linha 12) e a exibe (linha 13). Os resultados são os seguintes:
Comentários:
- o método [main] solicita uma referência ao bean [voiture1] (linha 12). O Spring inicia a criação do bean [voiture1], pois esse bean ainda não foi criado (singleton). Como o bean [voiture1] faz referência ao bean [personne2], este último bean é, por sua vez, construído. O bean [personne2] foi criado. Seu método [init] é então executado (linha 1) dos resultados. O bean [voiture1] é, em seguida, instanciado. Seu método [init] é então executado (linha 2) dos resultados.
- A linha 3 dos resultados provém da linha 13 de [main]: o valor do bean [voiture1] é exibido.
- A linha 15 de [main] solicita a destruição de todos os beans existentes, o que faz com que as linhas 4 e 5 dos resultados sejam exibidas.
15.2.5. Exemplo 3
Os elementos do exemplo 3 estão localizados no pacote [springioc03] do projeto:

O pacote [springioc03] é obtido inicialmente por meio de copiar/colar do pacote [springioc01]; em seguida, adiciona-se a ele a classe [GroupePersonnes], exclui-se a classe [Voiture] e adapta-se a classe [Main] ao novo exemplo.
A classe [GroupePersonnes] é a seguinte:
Seus dois membros privados são:
linha 8: membros: um array de pessoas que fazem parte do grupo
linha 9: groupesDeTravail: um dicionário que atribui uma pessoa a um grupo de trabalho
Observe-se aqui que a classe [GroupePersonnes] não define um construtor sem argumentos. Vale lembrar que, na ausência de qualquer construtor, existe um construtor “padrão”, que é o construtor sem argumentos e que não realiza nenhuma ação.
O objetivo aqui é mostrar como o Spring permite inicializar objetos complexos, tais como objetos que possuem campos do tipo matriz ou dicionário. O arquivo de beans [spring-config-03.xml] do exemplo 3 é o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="personne1" class="istia.st.springioc03.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Simon" />
<property name="age" value="40" />
</bean>
<bean id="personne2" class="istia.st.springioc03.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Brigitte" />
<property name="age" value="20" />
</bean>
<bean id="groupe1" class="istia.st.springioc03.GroupePersonnes" init-method="init" destroy-method="close">
<property name="membres">
<list>
<ref local="personne1" />
<ref local="personne2" />
</list>
</property>
<property name="groupesDeTravail">
<map>
<entry key="Brigitte" value="Marketing" />
<entry key="Simon" value="Ressources humaines" />
</map>
</property>
</bean>
</beans>
- linhas 14-17: a tag <list> permite inicializar um campo do tipo array ou que implemente a interface List com diferentes valores.
- linhas 20-23: a tag <map> permite fazer o mesmo com um campo que implemente a interface Map.
Para nossos testes, utilizaremos a seguinte classe [Main]:
- linhas 12-13: solicitamos ao Spring uma referência ao bean [groupe1] e exibimos o valor deste.
Os resultados obtidos são os seguintes:
Comentários:
- na linha 12 do [Main], solicita-se uma referência ao bean [groupe1]. O Spring inicia a criação desse bean. Como o bean [groupe1] faz referência aos beans [personne1] e [personne2], esses dois beans são criados (linhas 1 e 2 dos resultados). Em seguida, o bean [groupe1] é instanciado e seu método [init] é executado (linha 3 dos resultados).
- A linha 13 de [Main] exibe a linha 4 dos resultados.
- A linha 15 de [Main] exibe as linhas 5 a 7 dos resultados.
15.3. Configuração de uma aplicação n-tier com Spring
Consideremos uma aplicação de três camadas com a seguinte estrutura:
usuário
Dados
Camada de negócios [metier]
Camada de acesso aos dados [dao]
Camada de interface do usuário [ui]
Propomos aqui demonstrar a utilidade do Spring na construção dessa arquitetura.
- As três camadas serão tornadas independentes graças ao uso de interfaces Java
- A integração das três camadas será realizada pelo Spring
A estrutura da aplicação no Eclipse poderia ser a seguinte:
![]() |
- [1]: a camada [dao]:
- [IDao]: a interface da camada
- [Dao1, Dao2]: duas implementações dessa interface
- [2]: a camada [metier]:
- [IMetier]: a interface da camada
- [Metier1, Metier2]: duas implementações dessa interface
- [3]: a camada [ui]:
- [IUi]: a interface da camada
- [Ui1, Ui2]: duas implementações dessa interface
- [4]: os arquivos de configuração Spring do aplicativo. Vamos configurar o aplicativo de duas maneiras.
- [5]: as bibliotecas necessárias para o aplicativo. São as mesmas utilizadas nos exemplos anteriores.
- [6]: o pacote de testes. [Main1] utilizará a configuração [spring-config-01.xml] e [Main2] utilizará a configuração [spring-config-02.xml].
O objetivo deste exemplo é mostrar que podemos alterar a implementação de uma ou mais camadas da aplicação sem causar impacto nas demais camadas. Tudo ocorre no arquivo de configuração do Spring.
A camada [dao]
A camada [dao] implementa a seguinte interface [IDao]:
A implementação [Dao1] será a seguinte:
A implementação [Dao2] será a seguinte:
A camada [métier]
A camada [métier] implementa a seguinte interface [IMetier]:
A implementação [Metier1] será a seguinte:
A implementação [Metier2] será a seguinte:
A camada [ui]
A camada [ui] implementa a seguinte interface [IUi]:
A implementação [Ui1] será a seguinte:
A implementação [Ui2] será a seguinte:
Os arquivos de configuração do Spring
O primeiro, [spring-config-01.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- a classe dao -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao1"/>
<!-- a classe de negócios -->
<bean id="metier" class="istia.st.springioc.troistier.metier.Metier1">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- a classe UI -->
<bean id="ui" class="istia.st.springioc.troistier.ui.Ui1">
<property name="metier">
<ref local="metier" />
</property>
</bean>
</beans>
O segundo, [spring-config-02.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- a classe DAO -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao2"/>
<!-- a classe de negócios -->
<bean id="metier"
class="istia.st.springioc.troistier.metier.Metier2">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- a classe UI -->
<bean id="ui" class="istia.st.springioc.troistier.ui.Ui2">
<property name="metier">
<ref local="metier" />
</property>
</bean>
</beans>
Os programas de teste
O programa [Main1] é o seguinte:
O programa [Main1] utiliza o arquivo de configuração [spring-config-01.xml] e, portanto, as implementações [Ui1, Metier1, Dao1] das camadas. Os resultados obtidos no console do Eclipse:
O programa [Main2] é o seguinte:
O programa [Main2] utiliza o arquivo de configuração [spring-config-02.xml] e, portanto, as implementações [Ui2, Metier2, Dao2] das camadas. Os resultados obtidos no console do Eclipse:
15.4. Conclusion
O aplicativo que criamos possui grande flexibilidade de evolução. É possível alterar a implementação de uma camada por meio de uma simples configuração. O código das demais camadas permanece inalterado. Isso é possível graças ao conceito IoC, que é um dos dois pilares do Spring. O outro pilar é o AOP (Programação Orientada a Aspectos), que ainda não apresentamos. Ele permite adicionar, também por meio de configuração, “comportamento” a um método de classe sem alterar o código da mesma.






