15. Spring IoC
15.1. Introdução
O nosso objetivo é explorar as capacidades 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 Controlo), também conhecido como Injeção de Dependências
Considere a aplicação de três camadas que acabámos de construir:
![]() |
Para responder aos pedidos dos utilizadores, o controlador [Application] deve comunicar com a camada [service]. No nosso exemplo, tratava-se de uma instância do tipo [DaoImpl]. O controlador [Application] obteve uma referência à camada [service] no seu método [init] (Secção 14.8.3):
- linha 6: a camada [dao] foi instanciada através da criação explícita de uma instância de [DaoImpl]
- linha 9: a camada [service] foi instanciada através da criação explícita de uma instância de [ServiceImpl]
Recorde-se que as classes [DaoImpl] e [ServiceImpl] implementam interfaces, especificamente as interfaces [IDao] e [IService], respetivamente. Em versões futuras, a interface [IDao] será implementada por uma classe que gere uma lista de pessoas armazenadas numa base de dados. Vamos chamar a esta classe [DaoBD] para efeitos deste exemplo. Substituir a implementação [DaoImpl] da camada [dao] pela implementação [DaoBD] exigirá a recompilação da camada [web]. De facto, a linha 6 acima, que instancia a camada [dao] com um tipo [DaoImpl], deve agora instanciá-la com um tipo [DaoBD]. A nossa camada [web] depende, portanto, da camada [dao]. A linha 9 acima mostra que ela também depende da camada [service].
O Spring IoC permitirá-nos criar uma aplicação de 3 camadas onde as camadas são independentes umas das outras, ou seja, alterar uma não requer a alteração das outras. Isto proporciona uma grande flexibilidade na evolução da aplicação.
A arquitetura anterior evoluirá da seguinte forma:
![]() |
Com o [Spring IoC], o controlador [Application] obterá a referência de que necessita da camada [service] da seguinte forma:
- no seu método [init], solicitará à camada [Spring IoC] que forneça uma referência à camada [service]
- O [Spring IoC] utilizará então um ficheiro XML de configuração que lhe indica qual a classe que deve ser instanciada e como deve ser inicializada.
- O [Spring IoC] devolve a referência à camada [service] criada ao controlador [Application].
A vantagem desta solução é que os nomes das classes que instanciam as várias camadas já não estão codificados de forma rígida no método [init] do controlador, mas são simplesmente listados num ficheiro de configuração. Alterar a implementação de uma camada exigirá uma alteração neste ficheiro de configuração, mas não no controlador.
Vamos agora explorar as capacidades do [Spring IoC] através de exemplos.
15.2. Spring IoC na prática
15.2.1. Spring
O [Spring IoC] faz parte de um projeto mais vasto disponível em [http://www.springframework.org/] (maio de 2006):
![]() | ![]() |
![]() |
- [1]: O Spring utiliza várias tecnologias de terceiros, aqui referidas como dependências. Deve descarregar a versão com dependências para evitar ter de descarregar as bibliotecas de ferramentas de terceiros posteriormente.
- [2]: A estrutura de diretórios do ficheiro zip descarregado
- [3]: A distribuição [Spring], ou seja, os arquivos .jar do próprio projeto Spring sem as suas dependências. O aspecto [IoC] do Spring é fornecido pelos arquivos [spring-core.jar, spring-beans.jar].
- [4,5]: os arquivos de ferramentas de terceiros
15.2.2. Projetos Eclipse para os exemplos
Iremos criar três exemplos que ilustram a utilização do Spring IoC. Todos eles farão parte do seguinte projeto Eclipse:

O projeto [springioc-examples] está configurado de forma a que os ficheiros fonte e as classes compiladas se encontrem na raiz da pasta do projeto:
![]() |
- [1]: A estrutura da pasta do projeto [Eclipse]
- [2]: Os ficheiros de configuração do Spring estão localizados na raiz do projeto e, portanto, no classpath da aplicação
- [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] podem ser encontradas na pasta [dist] da distribuição Spring, e [commons-logging.jar] na pasta [lib/jakarta-commons]. Estes três arquivos foram incluídos no classpath da aplicação.
15.2.3. Exemplo 1
Os elementos do Exemplo 1 foram colocados no pacote [springioc01] do projeto:
![]()
A classe [Person] é a seguinte:
A classe contém:
- linhas 6-7: dois campos privados, name e age
- linhas 23–38: os métodos get e set para estes dois campos
- linhas 10-12: um método toString para recuperar o valor do objeto [Person] como uma string
- linhas 15-21: um método init que será chamado pelo Spring quando o objeto for criado, e um método close que será chamado quando o objeto for destruído
Para instanciar objetos do tipo [Person] usando o Spring, utilizaremos o seguinte ficheiro [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 ficheiros de configuração do Spring. Dentro desta tag, a tag <bean> é utilizada para definir os vários objetos a serem criados.
- linhas 4–7: definição de um bean
- linha 4: o bean é denominado [person1] (atributo id) e é uma instância da classe [istia.st.springioc01.Person] (atributo class). O método [init] da instância será chamado assim que esta for criada (atributo init-method), e o método [close] da instância será chamado antes de esta ser destruída (atributo destroy-method).
- linha 5: define o valor a ser atribuído à propriedade [name] (atributo name) da instância [Person] criada. Para realizar esta inicialização, o Spring utilizará o método [setName]. Este método deve, portanto, existir. É o que acontece aqui.
- Linha 6: O mesmo que acima para a propriedade [age].
- Linhas 8–11: Definição semelhante de um bean denominado [person2]
A classe de teste [Main] é a seguinte:
Comentários:
- linha 10: para recuperar os beans definidos no ficheiro [spring-config-01.xml], utilizamos um objeto [XmlBeanFactory], que nos permite instanciar os beans definidos num ficheiro XML. O ficheiro [spring-config-01.xml] será colocado no [ClassPath] da aplicação, ou seja, num dos diretórios pesquisados pela Máquina Virtual Java quando procura uma classe referenciada pela aplicação. O objeto [ClassPathResource] é utilizado para procurar um recurso no [ClassPath] de uma aplicação, neste caso o ficheiro [spring-config-01.xml]. O objeto [bf] (Bean Factory) resultante permite-nos obter uma referência a um bean denominado "XX" utilizando a instrução bf.getBean("XX").
- Linha 12: É solicitada uma referência para o bean denominado [person1] no ficheiro [spring-config-01.xml].
- Linha 13: O valor do objeto [Person] correspondente é exibido.
- Linhas 15–16: Fazemos o mesmo para o bean chamado [person2].
- Linhas 18–19: Solicitamos novamente o bean denominado [person2].
- Linha 21: Todos os beans em [bf] são removidos, ou seja, aqueles criados a partir do ficheiro [spring-config-01.xml].
A execução da classe [Main] produz os seguintes resultados:
Comentários:
- A linha 1 foi obtida ao executar a linha 12 de [Main]. A operação
forçou a criação do bean [person1]. Como [init-method="init"] estava escrito na definição do bean [person1], o método [init] do objeto [Person] criado foi executado. A mensagem correspondente é exibida.
- Linha 2: A linha 13 de [Main] exibiu o valor do objeto [Person] criado.
- Linhas 3–4: O mesmo processo repete-se para o bean denominado [person2].
- Linha 5: A operação nas linhas 18–19 de [Main]
personne2 = (Personne) bf.getBean("personne2");
System.out.println("personne2=" + personne2.toString());
não provocou a criação de um novo objeto do tipo [Person]. Se assim fosse, o método [init] teria sido exibido. Este é o princípio do singleton. Por predefinição, o Spring cria apenas uma única instância dos beans no seu ficheiro de configuração. Trata-se de um serviço de referência a objetos. Se lhe for solicitada a referência a um objeto que ainda não tenha sido criado, ele cria-o e devolve uma referência. Se o objeto já tiver sido criado, o Spring devolve simplesmente uma referência ao mesmo. Aqui, uma vez que [person2] já foi criado, o Spring devolve simplesmente uma referência ao mesmo.
- A saída nas linhas 6–7 foi desencadeada pela linha 21 de [Main], que solicita a destruição de todos os beans referenciados pelo objeto [XmlBeanFactory bf], nomeadamente os beans [person1] e [person2]. Como estes dois beans têm o atributo [destroy-method="close"], o método [close] de ambos os beans é executado, fazendo com que as linhas 6–7 sejam exibidas.
Agora que abordámos os conceitos básicos de uma configuração Spring, poderemos avançar nas nossas explicações um pouco mais rapidamente.
15.2.4. Exemplo 2
Os elementos do Exemplo 2 estão localizados no pacote [springioc02] do projeto:

O pacote [springioc02] é criado primeiro através da cópia e colagem do pacote [springioc01]; em seguida, a classe [Car] é adicionada a ele e a classe [Main] é adaptada ao novo exemplo.
A classe [Car] é a seguinte:
A classe contém:
- linhas 5–7: três campos privados: type, make e owner. Estes campos podem ser inicializados e lidos utilizando os métodos públicos get e set nas linhas 26–48. Também podem ser inicializados utilizando o construtor Car(String, String, Person) definido nas linhas 13–17. A classe também possui um construtor sem argumentos para cumprir com o padrão JavaBean.
- linhas 20–23: um método toString para recuperar o valor do objeto [Car] como uma string
- Linhas 51–57: um método `init` que será chamado pelo Spring imediatamente após a criação do objeto, e um método `close` que será chamado quando o objeto for destruído
Para criar objetos do tipo [Car], utilizaremos o seguinte ficheiro 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>
Este ficheiro adiciona um bean com a chave "car1" do tipo [Car] aos beans definidos em [spring-config-01.xml] (linhas 12–17). Para inicializar este bean, poderíamos ter escrito:
Em vez de escolher o método já apresentado no Exemplo 1, optámos por utilizar aqui o construtor Car(String, String, Person) da classe.
- linha 12: definição do nome do bean, da sua classe, do método a executar após a sua instanciação e do método a executar após a sua destruição.
- linha 13: valor do primeiro parâmetro do construtor [Car(String, String, Person)].
- Linha 14: valor do segundo parâmetro do construtor [Car(String, String, Person)].
- Linhas 15–17: valor do terceiro parâmetro do construtor [Car(String, String, Person)]. Este parâmetro é do tipo [Person]. Atribuímos-lhe a referência (etiqueta ref) do bean [person2] definido no mesmo ficheiro (atributo local).
Para os nossos testes, utilizaremos a seguinte classe [Main]:
O método [main] recupera a referência ao bean [car1] (linha 12) e apresenta-a (linha 13). Os resultados são os seguintes:
Comentários:
- O método [main] solicita uma referência ao bean [car1] (linha 12). O Spring começa a criar o bean [car1] porque este ainda não foi criado (singleton). Como o bean [car1] faz referência ao bean [person2], este último é, por sua vez, construído. O bean [person2] foi criado. O seu método [init] é então executado (linha 1) dos resultados. O bean [car1] é então instanciado. O 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 [car1] é apresentado.
- 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] é criado primeiro através da cópia e colagem do pacote [springioc01]; em seguida, a classe [GroupePersonnes] é adicionada, a classe [Voiture] é removida e a classe [Main] é adaptada ao novo exemplo.
A classe [GroupePersonnes] é a seguinte:
Os seus dois membros privados são:
linha 8: members: uma matriz de pessoas que são membros do grupo
linha 9: workingGroups: um dicionário que associa uma pessoa a um grupo de trabalho
Note-se aqui que a classe [PersonGroup] não define um construtor sem argumentos. Recorde-se que, na ausência de qualquer construtor, existe um construtor «padrão», que é o construtor sem argumentos que não faz nada.
O objetivo aqui é demonstrar como o Spring permite inicializar objetos complexos, tais como aqueles com campos de matriz ou dicionário. O ficheiro de bean [spring-config-03.xml] para o 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-lhe inicializar um campo do tipo array ou um que implemente a interface List com valores diferentes.
- linhas 20-23: a tag <map> permite fazer o mesmo com um campo que implemente a interface Map.
Para os nossos testes, utilizaremos a seguinte classe [Main]:
- Linhas 12-13: Solicitamos ao Spring uma referência ao bean [group1] e exibimos o seu valor.
Os resultados são os seguintes:
Comentários:
- Na linha 12 do [Main], é solicitada uma referência ao bean [group1]. O Spring começa a criar este bean. Como o bean [group1] faz referência aos beans [person1] e [person2], estes dois beans são criados (linhas 1 e 2 dos resultados). O bean [group1] é então instanciado e o 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–7 dos resultados.
15.3. Configurar uma aplicação de n camadas com o Spring
Considere uma aplicação de 3 camadas com a seguinte estrutura:
Camada de Dados do Utilizador [business]Camada de Acesso aos Dados [dao]Camada de Interface do Utilizador [ui]
Aqui, pretendemos demonstrar as vantagens de utilizar o Spring para construir uma arquitetura deste tipo.
- As três camadas serão tornadas independentes através da utilização de interfaces Java
- A integração das três camadas será gerida pelo Spring
A estrutura da aplicação no Eclipse poderá ser a seguinte:
![]() |
- [1]: a camada [DAO]:
- [IDao]: a interface da camada
- [Dao1, Dao2]: duas implementações desta interface
- [2]: a camada [business]:
- [IMetier]: a interface da camada
- [Business1, Business2]: duas implementações desta interface
- [3]: a camada [ui]:
- [IUi]: a interface da camada
- [Ui1, Ui2]: duas implementações desta interface
- [4]: os ficheiros de configuração Spring da aplicação. Vamos configurar a aplicação de duas formas.
- [5]: as bibliotecas necessárias para a aplicação. Estas são as utilizadas nos exemplos anteriores.
- [6]: o pacote de testes. [Main1] utilizará a configuração [spring-config-01.xml] e [Main2] 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 qualquer impacto nas outras camadas. Tudo acontece no ficheiro 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 [de negócios]
A camada [de negócios] implementa a seguinte interface [IMetier]:
A implementação de [Business1] será a seguinte:
A implementação do [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:
Ficheiros 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>
<!-- the dao class -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao1"/>
<!-- the business class -->
<bean id="metier" class="istia.st.springioc.troistier.metier.Metier1">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- the UI class -->
<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>
<!-- the dao class -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao2"/>
<!-- the business class -->
<bean id="metier"
class="istia.st.springioc.troistier.metier.Metier2">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- the UI class -->
<bean id="ui" class="istia.st.springioc.troistier.ui.Ui2">
<property name="metier">
<ref local="metier" />
</property>
</bean>
</beans>
Programas de teste
O programa [Main1] é o seguinte:
O programa [Main1] utiliza o ficheiro de configuração [spring-config-01.xml] e, por conseguinte, as implementações [Ui1, Metier1, Dao1] das camadas. Os resultados apresentados na consola do Eclipse:
O programa [Main2] é o seguinte:
O programa [Main2] utiliza o ficheiro de configuração [spring-config-02.xml] e, por conseguinte, as implementações [Ui2, Metier2, Dao2] das camadas. Os resultados apresentados na consola do Eclipse:
15.4. Conclusão
A aplicação que criámos é altamente escalável. Podemos alterar a implementação de uma camada simplesmente configurando-a. O código das outras camadas permanece inalterado. Isto é conseguido através do conceito de IoC, que é um dos dois pilares do Spring. O outro pilar é o AOP (Programação Orientada a Aspectos), que ainda não abordámos. Permite adicionar «comportamento» a um método de classe — também através de configuração — sem modificar o código do método.






