-spring-framework
-spring-framework
br
Spring Framework
Sobre o Autor
. /0 , ( " , 1' 2 3 )
, & 4 + 56
7 89 49 0' 8,0: , ; 7 56 98 6 0 , ; , <0 ;
' , ,' ' , ; , , , ; ;
Você pode:
Uso Não-Comercial. Você não pode utilizar esta obra com finalidades
comerciais.
• Para cada novo uso ou distribuição, você deve deixar claro para outros os termos da licença desta
obra.
• Qualquer uma destas condições podem ser renunciadas, desde que Você obtenha permissão do
autor.
Qualquer direito de uso legítimo (ou "fair use") concedido por lei, ou qualquer outro direito protegido
pela legislação local, não são em hipótese alguma afetados pelo disposto acima.
Este é um sumário para leigos da Licença Jurídica (na íntegra).
1. Introdução
Java é a plataforma tecnológica de maior sucesso na história da computação. De uma simples linguagem
para a criação de pequenos aplicativos para set-top boxes (como as utilizada pela TV digital interativa), se
tornou uma plataforma completa, abrangendo desde programas para pequenos dispositivos até as maiores
aplicações coorporativas do mundo.
A maioria das aplicações desenvolvidas em Java hoje são focadas no mercado coorporativo. Estas
aplicações geralmente possuem características básicas em comum: interface web, persistência em banco
de dados relacional, comunicação com outros sistemas, necessidade de autenticação e autorização de
usuários, etc.
Para padronizar o desenvolvimento e os produtos disponíveis, o consórcio que rege a plataforma Java, Java
Community Process (JCP), criou a macro-especificação Java 2 Enterprise Edition (J2EE). Esta reúne
diversas especificações menores que ditam sobre como as características de aplicações coorporativas são
implementadas em Java, por exemplo como uma aplicação web em Java deve ser ou como acessar o
banco de dados nesta linguagem.
Como a plataforma Java é utilizada universalmente, todo tipo de aplicação é desenvolvida nela. J2EE foi
criada visando suportar aplicações de grande porte e exerce muito bem seu papel neste cenário, mas acaba
trazendo muita complexidade para o desenvolvimento de aplicações não tão grandes. Ocorre que a grande
maioria das aplicações existentes hoje não é de grande porte e não requer todos estes recursos, tendo
ainda assim que pagar o preço da complexidade de uma plataforma "Tamanho Único”.
Para amenizar e reduzir este problema, surgiram alternativas vindas da comunidade de desenvolvedores
Java. Soluções simples criadas por pequenos grupos se espalharam e tornaram-se um padrão de facto.
Uma destas soluções alternativas é o Spring Framework, criado por Rod Johnson para amenizar os
problemas que este encontrava ao utilizar a tecnologia de Enterprise Java Beans (EJB), parte principal do
J2EE.
A prova da força das soluções alternativas veio com a versão 5.0 de J2EE, agora chamado apenas Java
Enterprise Edition (Java EE). Esta traz recursos que claramente foram inspirados tanto no Spring como em
outras soluções alternativas. É a padronização de técnicas e tecnologias que a comunidade de
desenvolvedores consagrou nos últimos anos.
Esta apostila é parte integrante de um curso sobre o Spring Framework, em sua versão 1.2. Seu conteúdo
visa explicar e treinar o leitor no uso desta ferramenta.
Apesar do lançamento da solução padronizada trazida pelo Java EE 5.0, espera-se que a adoção desta
plataforma pelas empresas demore um bom tempo. Além da necessidade de treinar todos os recursos
humanos nesta nova plataforma, existe a necessidade de atualizar os servidores de aplicação e máquinas
virtuais (Java EE 5.0 só funciona em Java Virtual Machines 5.0). Utilizando o Spring Framework e outras
soluções alternativas, podemos ter os benefícios da nova versão do Java EE em servidores de aplicação e
JVMs antigas, a partir da versão 1.3 de Java.
Instanciação Direta
A forma mais natural de se obter uma referência para o UsuarioDao de dentro de
GerenciadorUsuarios seria instanciando-a diretamente, como no exemplo abaixo:
public class GerenciadorUsuarios {
Instanciação Direta é quando uma classe cria o objeto de que precisa. Em Java isso geralmente é feito com
o operador new.
Acontece que utilizando esta forma para componentes de aplicações corporativas é criada uma
dependência muito grande entre os objetos. A classe GerenciadorUsuarios vai precisar saber como
iniciar a UsuarioDao para que esta possa operar. Vamos supor que a UsuarioDao necessite de alguma
configuração para ser utilizada, esta configuração terá que ser feita no construtor da classe
GerenciadorUsuarios:
public class GerenciadorUsuarios {
public GerenciadorUsuario(){
//configura o DAO
Uso de Registry
Existem diversas soluções possíveis para este problema, até a versão 1.4, J2EE utiliza a solução de
registrar os recursos num diretório de componentes chamado JNDI (Java Naming and Directory Interface).
Esta solução é baseada no Padrão Arquitetural Registry. Ao utilizar o Registry, temos um lugar comum onde
as classes podem obter referências a outras classes.
Registry é um Padrão Arquitetural onde componentes são obtidos através da consulta a um lugar comum.
Em J2EE geralmente utiliza-se a árvore JNDI.
No exemplo, poderíamos fazer GerenciadorUsuarios obter uma referência ao UsuarioDao por meio
do Registry JNDI. Primeiro, a classe UsuarioDao precisa ser registrada (o que é chamado de bind),
geralmente isto é feito pelo container utilizando arquivos de configuração (deployment descriptors) em um
código parecido com este:
//configura o DAO
UsuarioDao usuarioDao = new UsuarioDao();
usuarioDao.setServidorBancoDeDados("mysql.fragmental.com.br:3306");
usuarioDao.setUsuario("pcalcado");
usuarioDao.setSenha("jap123");
usuarioDao.setSchema("curso-spring");
usuarioDao.setDriverJdbc("com.mysql.jdbc.Driver");
//... e mais configurações
//Registra o DAO
Context ctx = new InitialContext();
ctx.bind("usuarioDao", usuarioDao);
Logo depois, a nossa classe pode obter uma referência do Registry, processo chamado de lookup:
public GerenciadorUsuarios(){
try{
Context ctx = new InitialContext();
usuarioDao = (UsuarioDao) ctx.lookup("usuarioDao");
}catch(NamingException ex){
//tratar exceção
}
}
Dependency Injection
A pedra fundamental do Spring Framework é o conceito de Dependency Injection.
Dependency Injection (Injeção de Dependências - DI) é uma forma de Inversion Of Control (Inversão de
Controle – IoC) na qual um componente não instancia suas dependências mas o ambiente (container)
automaticamente as fornece.
Este é o Princípio de Hollywood: “Não me ligue, eu te ligo”, ou seja: o objeto não chama o container, o
container chama o objeto. No exemplo, a classe GerenciadorUsuarios poderia ser implementada
exatamente desta forma:
Usos Indicados
Cada uma das formas de implementação de dependências mostradas acima possui cenários onde sua
aplicação é ótima.
Para componentes ou Services, o uso de instanciação direta é altamente desaconselhável. Componentes
geralmente são classes de granularidade grossa que fornecem serviços a outros componentes e como visto
o uso de instanciação direta faria com que um componente criasse um acoplamento muito grande com
outro.
O uso de Registry para componentes é útil para lidar com o legado de aplicações J2EE. Como veremos, o
Spring também pode trabalhar com JNDI o que facilita muito esta integração. Alguns componentes de
infraestrutura também podem tirar proveito desta técnica. De uma maneira geral, componentes de
aplicação, que não fazem parte da infraestrutura, têm maior benefícios quando implementadas utilizando
Dependency Injection.
Da mesma forma, o uso de Registry ou Dependency Injection para implementar a relação entre duas
classes simples é um erro na maioria das vezes. Classes simples, principalmente se persistentes, devem
utilizar instanciação direta.
Exercícios
1 – Dado o diagrama de classes abaixo, implemente a dependência utilizando as três formas descritas
neste capítulo.
2 – Dado o diagrama abaixo, identifique a melhor estratégia de implementação de cada uma das relações
mostradas.
/**
* Exibe uma mensagem de texto.
*
* @param mensagem
*/
public void exibeMensagem(String mensagem) {
// No nosso exemplo, vamos exibir a mensagem na saída principal
System.out.println("[MONITOR] "+mensagem);
}
/**
* Lê um tesxto do usuário.
*
* @return
*/
public String ler() {
//imprime um 'prompt'
System.out.print("[TECLADO]>");
try {
texto = new BufferedReader(new InputStreamReader(System.in))
.readLine();
} catch (IOException e) {
System.out.println("Erro lendo teclado!");
e.printStackTrace();
}
return texto;
}
}
Note que não existe código de inicialização das outras classes na classe Computador. Se instanciarmos
esta classe manualmente e executarmos o método ligar() teremos uma NullPointerException já
que os componentes que ele necessita não foram fornecidos.
Para testarmos nossa implementação, podemos criar uma classe que inicializa e supre todas as
dependências, como a descrita abaixo:
public class IniciaComputador {
public static void main(String[] args) {
Computador computador = new Computador();
computador.setImpressora(new Impressora());
computador.setTeclado(new Teclado());
computador.setMonitor(new Monitor());
computador.ligar();
}
}
Esta classe é útil para testes unitários (um conceito que veremos adiante), mas para o uso na aplicação real
vamos deixar este trabalho para o Spring.
Para fazer o Spring entender a dependência entre nossas classes, precisamos criar um arquivo de
configuração. Este geralmente recebe o nome de applicationContext.xml. Vejamos o arquivo que
define nosso exemplo:
<?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="computadorBean"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.Computador">
<bean id="impressoraBean"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.Impressora"/>
<bean id="tecladoBean"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.Teclado"/>
<bean id="monitorBean"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.Monitor"/>
</beans>
E para testar nosso exemplo utilizando Spring podemos utilizar a classe abaixo:
public class IniciaUsandoSpring {
O Arquivo ApplicationContext.xml
O applicationContext.xml é onde são declarados os beans do Spring. O framework chama de beans
todas as classes que gerencia. As classes precisam ser declaradas utilizando o elemento <bean>, seguindo
o formato:
<bean id=”identificador do bean” class=”FQN da classe que implementa o bean” >
<property name=”nome do atributo” ref=”id do bean que satisfaz a
dependência” />
</bean>
FQN significa Fully-Qualified Name, ou nome completo. O FQN de uma classe é o nome da classe com o
seu pacote completo. Por exemplo, o FQN da classe String é java.lang.String, o FQN da classe
List é java.util.List.
}
Que recebe uma lista de Alunos como argumento em seu construtor. A classe Aluno é bem simples:
public class Aluno {
private String nome = null;
<bean id="bernarda"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.Aluno">
<property name="nome"><value>Bernarda</value></property>
</bean>
<bean id="phillip"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.Aluno">
<property name="nome"><value>Phillip</value></property>
</bean>
<bean id="valentina"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.Aluno">
<property name="nome"><value>Valentina</value></property>
</bean>
Simplesmente definimos um elemento <list> contendo os beans desejados na ordem desejada.
Caso o objeto receba um mapa (java.util.Map), também existe uma sintaxe facilitando sua
configuração:
public class Dicionario {
this.definicoes = definicoes;
}
<bean id="dicionario"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.Dicionario">
<property name="definicoes">
<map>
<entry key="mother"><value>Mãe</value></entry>
<entry key="father"><value>Pai</value></entry>
</map>
</property>
</bean>
Existe ainda uma terceira facilidade, muito útil quando utilizamos o Spring em conjunto com outros
frameworks, que é a definição de Properties (java.util.Properties). Properties são o mecanismo
padrão na plataforma Java para a criação de arquivos de configuração simples, o Spring permite que você
crie esta configuração diretamente no applicationContext.xml, sem necessidade de arquivos
externos. Por exemplo, dada esta classe configurada via um arquivo Properties:
public class ConexaoBancoDeDados {
this.usuario = properties.getProperty("banco.usuario");
this.senha = properties.getProperty("banco.senha");
Exercícios
1 – Crie arquivos de configuração do Spring para os cenários descritos nos exercícios do capítulo anterior.
2 – Escreva classes correspondentes ao arquivo de configuração descrito abaixo:
<bean id="A"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.exercícios.ClasseA" />
<bean id="B"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.exercícios.ClasseB">
<property name="teste" ref="A" />
</bean>
<bean id="C"
class="br.com.fragmental.cursos.spring.apostila.capitulo3.exercícios.ClasseC">
<property name="lista"><list>
<ref bean="A" />
<ref bean="B" /></list>
</bean>
Entretanto, ainda existem e existirão por muito tempo limitações técnicas que fazem com que os conceitos
de negócio que modelamos sofram “invasão” de conceitos técnicos, como autenticação de usuários,
transações com bancos de dados e arquivos de log. Estes conceitos não fazem parte do domínio do
problema, existem apenas para satisfazer a necessidade do software.
Normalmente, o que se faz com OOP é simplesmente misturar estes conceitos, criando código que mistura
regras de negócio com limitações tecnológicas. Por exemplo, o método abaixo traz o código de negócios
necessário para aprovar um empréstimo no exemplo acima.
public void aprovar() throws MaximoPrestacoesExcedidoException,
CreditoExcedidoException{
//Checa se o empréstimo excede o limite do cliente
if(!cliente.podeEmprestar(this.valor)){
this.status=Status.NEGADO;
throw new CreditoExcedidoException("Emprestimo muito alto");
}
}
//se passou nas checagens:
//empréstimo está aprovado
this.status=Status.APROVADO;
}
Para que este código tire proveito dos recursos de aplicações modernas, por exemplo para que possa
participar de transações no banco de dados e criar registros em um arquivo de log, ele precisa ser
modificado para incluir código que lida exclusivamente com isso, como abaixo.
public void aprovar() throws MaximoPrestacoesExcedidoException,
CreditoExcedidoException, SQLException, NamingException {
this.status = Status.NEGADO;
throw new CreditoExcedidoException("Emprestimo muito alto");
}
this.status = Status.NEGADO;
throw new MaximoPrestacoesExcedidoException(
"O empréstimo deve ser pago em até "
+ Prestacao.MAXIMO_PRESTACOES + " vezes!");
}
+ "] aprovado");
// empréstimo está aprovado
this.status = Status.APROVADO;
conexao.commit();
}
A parte destacada em amarelo é o código adicionado exclusivamente para que o trecho de código tenha
acesso aos recursos tecnológicos. Como visto, o código de negócios, que é o que realmente importa ao
criar um software, fica soterrado no meio do código de infra-estrutura, que existe apenas para satisfazer
necessidades técnicas.
Mesmo neste exemplo extremamente simples o código de infra-estrutura já representa boa parte do código
da aplicação. Este problema faz com que mudanças em regras de negócio toquem o código de infra-
estrutura, e vice-versa. Não é tão fácil separar mudanças no aspecto não-funcional das funcionais, tudo
está no mesmo lugar!
Coesão é a métrica de quanto as linhas de código em um método (ou os métodos em uma classe) são
relacionados entre si. Um método que faz conexão com banco de dados e processa regras de negócio tem
baixa coesão.
Também existe o problema da repetição de código. Mesmo lidando apenas com código de negócios
podemos acabar tendo que repetir uma mesma rotina várias vezes, linha por linha. No exemplo abaixo,
vemos que ao ser criado um novo Emprestimo um Funcionario precisa ser notificado do fato.
public Emprestimo solicitarEmprestimo() {
Emprestimo novo = new Emprestimo(this);
notificadorFuncionarios.notificar(novo);
return novo;
}
Acontece que por uma mudança nos requisitos agora o funcionário deve ser avisado em qualquer alteração
no Status do Emprestimo, então ao invés de fazer simplesmente this.status= o código deve invocar
este método:
private void setStatus(Status novoStatus) {
this.status = novoStatus;
notificadorFuncionarios.notificar(novoStatus);
}
Os dois métodos, em classes diferentes, possuem rotinas para notificar o Funcionario. Não se trata
apenas de encapsular esta rotina num método, isso já foi feito (como o método notificar()). O fato
deste método ser invocado sempre e da mesma maneira por diversas classes é que pode se tornar um
problema quando a implementação mudar. O ideal seria conseguir “avisar” ao sistema de que toda vez que
os métodos solicitarEmprestimo() e setStatus() (e qualquer outro que precise desta
funcionalidade ) forem chamados deve ser disparada uma notificação.
Conceitos Ortogonais (Orthogonal Concerns) são os diversos conceitos com que um código de aplicação
tem que lidar, como regras de negócio, segurança e conexões com bancos de dados.
A idéia por trás de Aspect-Oriented Programming é tratar estes conceitos em lugares separados (entenda-
se “classes separadas” se estamos falando de Java) enquanto estamos desenvolvendo e uni-las em tempo
de execução. Estes conceitos que se integram ao código de negócios são chamados de Aspectos.
Aspectos são a separação do trecho de código que implementa conceitos ortogonais em classes distintas
que são unidas em tempo de execução (runtime).
Ao invés de um grande bloco de código que cuida de transações, segurança, conexão com o banco de
dados e ainda das regras de negócio nós podemos definir cada um destes aspectos em uma ou mais
classes diferentes e a ferramenta de AOP irá fazer esta mistura enquanto o programa é executado. O
diagrama abaixo ilustra os diversos aspectos separados enquanto estamos desenvolvendo (esquerda) e o
produto final em tempo de execução, a classe que mistura todos estes aspectos (direita).
Desenvolvimento Execução
Segurança
Segurança
Transações Transações
Log
Log
Negócios
Negócios
Figura 4.2 – Aspectos em Desenvolvimento e Execução
Claro que desta forma podemos também reaproveitar os aspectos em diversas classes. Geralmente você
vai precisar iniciar ou terminar transações, verificar se o usuário tem os privilégios necessários e demais
aspectos em diversas classes de negócio, utilizando AOP você pode criar aspectos apenas uma vez e liga-
los a diversas classes de negócio.
Aspectos são formados por:
• Advices - Código a ser executado para cumprir uma tarefa (abrir ou fechar uma conexão, por
exemplo)
• Pointcuts – Lugares na classe-alvo (a classe com regras de negócio) onde o Advice deve ser
invocado (por exemplo sempre que um método da classe UsuarioDao for chamado)
Ou seja, podemos ter um aspecto descrito como: “Toda vez que se invocar um método cujo nome comece
com ‘fechar’ (por exemplo ‘fecharVenda’) execute o código que inicia uma transação no banco de dados”.
Usos Indicados
AOP é uma técnica utilizada principalmente para duas coisas: prevenir repetição de código e remover
código de infra-estrutura do código da aplicação. Ao contrário do mito popular, não existe uma competição
entre AOP e OOP, uma complementa a outra. Também não há necessidade de utilizar OOP com técnicas
Exercícios
1 - O trecho de código abaixo foi marcado em áreas com conceitos ortogonais distintos. Identifique o
conceito implementado por cada área.
public class Noticia {
private Usuario usuario = null;
private SecaoDoSite secao = null;
private String titulo=null;
private String texto = null;
Nota: A Interface21, empresa que emprega a maioria dos desenvolvedores do Spring Framework, contratou
há algum tempo o criado do AspectJ, framework Java mais famoso e poderoso para AOP. A integração
entre estes frameworks já pode ser vista na versão 2.0 do Spring Framework, trazendo a maioria dos
recursos de AOP não presentes em versões anteriores.
Para entender como podemos implementar AOP no Spring vamos a um exemplo simples. Existe uma
classe responsável por salvar arquivos no disco rígido (como é um exemplo a parte que efetivamente
escreveria o arquivo em disco não está presente).
public class GerenciadorArquivos {
public void salvarArquivo(Diretorio diretorioASalvar, Arquivo
arquivoSalvo){
System.out.println("GerenciadorArquivos: SALVO
["+arquivoSalvo.getNome()+"] em ["+diretorioASalvar.getNome()+"]");
}
}
Esta classe é utilizada por outra responsável por publicar notícias, cada notícia viraria um arquivo.
public class PublicadorNoticias {
private GerenciadorArquivos gerenciadorArquivos = null;
public void setGerenciadorArquivos(GerenciadorArquivos
gerenciadorArquivos) {
this.gerenciadorArquivos = gerenciadorArquivos;
}
public void publicar(String titulo){
Arquivo arquivo = new Arquivo(titulo);
Diretorio diretorio = new Diretorio("/usr/local/reportagens");
gerenciadorArquivos.salvarArquivo(diretorio, arquivo);
}
}
Isso tudo é configurado no Spring como vimos em capítulos anteriores:
<?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="gerenciadorArquivo"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.GerenciadorArquivos"
/>
<bean id="publicadorNoticias"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.PublicadorNotici
as">
<property name="gerenciadorArquivos" ref="gerenciadorArquivo"/>
</bean>
</beans>
Para testar, utilizamos este trecho de código:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"classpath:br/com/fragmental/cursos/spring/apostila/capitulo3/applicationC
ontext-Capitulo3.xml");
Computador computador = (Computador)
applicationContext.getBean("computadorBean");
computador.ligar();
E temos como resultado impresso na saída do programa:
GerenciadorArquivos: SALVO [Lançado Spring Framework 2.0!] em
[/usr/local/reportagens]
Este é o fluxo padrão de negócios.
<bean id="gerenciadorArquivoTarget"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.GerenciadorArqui
vos"/>
<bean id="gerenciadorArquivo"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property
name="interceptorNames"><value>logAdvice</value></property>
<property name="target" ref="gerenciadorArquivoTarget" />
</bean>
<bean id="logAdvice"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.LogAdvice" />
<bean id="publicadorNoticias"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.PublicadorNotici
as">
<property name="gerenciadorArquivos" ref="gerenciadorArquivo"/>
</bean>
</beans>
Esta mistura é feita quando nós declaramos que o bean gerenciadorArquivos é agora uma instância de
ProxyFactoryBean. Esta classe fornecida pelo Spring Framework é configurada com o target que
definimos antes e a lista de advices que devem ser aplicados.
Target é a classe ‘crua’ que vai ser misturada com os advices para gerar uma classe modificada.
Nota: Para utilizar este exemplo você precisa da biblioteca CGLIB2 no Classpath. Esta biblioteca permite
que o Spring mude o código de classes e não trabalhe apenas com interfaces.
Existe também o After advice, que como o nome diz é executado após o método. Por exemplo, vamos
definir um advice do tipo After que notifica (escrevendo na tela neste exemplo) sobre a publicação de uma
nova notícia. Este advice será invocado após o método publicar() do PublicadorNoticias.
Primeiro, criamos o advice.
public class NotificacaoAdvice implements AfterReturningAdvice {
Notificador notificador = null;
public void afterReturning(Object valorDeRetorno, Method metodo,
Object[] argumentos, Object alvo) {
Arquivo arquivo = (Arquivo)argumentos[1];
notificador.notificarSobreNoticiaNova(arquivo.getNome());
}
class="br.com.fragmental.cursos.spring.apostila.capitulo5.GerenciadorArqui
vos"/>
<bean id="gerenciadorArquivo"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property
name="interceptorNames"><list><value>notificacaoAdvice</value>
<value>logAdvice</value></list></property>
<property name="target" ref="gerenciadorArquivoTarget" />
</bean>
<bean id="logAdvice"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.LogAdvice" />
<bean id="publicadorNoticias"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.PublicadorNotici
as">
<property name="gerenciadorArquivos" ref="gerenciadorArquivo"/>
</bean>
<bean id="notificador"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.Notificador" />
<bean id="notificacaoAdvice"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.NotificacaoAdvice">
<property name="notificador" ref="notificador" />
</bean>
</beans>
Como pode ser percebido nós utilizamos um elemento <list> para definir dois valores, os nomes dos
advices. Podem haver mais de um advice de cada tipo sem problemas.
Outro ponto interessante é que o advice em si é um bean gerenciador pelo Spring e pode receber injeção
de dependências: o notificacaoAdvice do exemplo é injetado com um notificador.
A saída do código de exemplo fica:
LOG --- Sat Aug 05 13:12:00 GMT-03:00 2006 --- Executádo método 'salvarArquivo'
utilizando como parâmetros
br.com.fragmental.cursos.spring.apostila.capitulo5.Diretorio@12a1e44
br.com.fragmental.cursos.spring.apostila.capitulo5.Arquivo@29428e
GerenciadorArquivos: SALVO [Lançado Spring Framework 2.0!] em
[/usr/local/reportagens]
Notificador: EXTRA EXTRA! 'Lançado Spring Framework 2.0!' !!!
Outro advice com comportamento muito parecido é o Throws, que é executado quando uma exceção é
lançada. Para utilizar este advice deve-se implementar a interface ThrowsAdvice.
}
}
}
Para termos este advice funcionando no nosso sistema basta configurarmos o
applicationContext.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="usuarioDaoTarget"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.UsuarioDao" />
<bean id="usuarioDao"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<value>verificaConexaoDisponivelAdvice</value>
</property>
<property name="target" ref="usuarioDaoTarget" />
</bean>
<bean id="verificaConexaoDisponivelAdvice"
class="br.com.fragmental.cursos.spring.apostila.capitulo5.VerificaConexaoDisponi
velAdvice" />
</beans>
Testando com o exemplo abaixo:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"classpath:br/com/fragmental/cursos/spring/apostila/capitulo5/applicationC
ontext-Capitulo5.xml");
UsuarioDao dao = (UsuarioDao) applicationContext.getBean("usuarioDao");
Usuario usuario = new Usuario("pcalcado");
// conexão fechada...
System.out.println("Primeiro exemplo");
Conexao conexao = new Conexao();
dao.salvar(usuario, conexao);
System.out.println("-------------------------");
// sem conexão...
System.out.println("Segundo exemplo");
dao.salvar(usuario, null);
System.out.println("-------------------------");
// conexão aberta...
System.out.println("Terceiro exemplo");
Conexao conexao2 = new Conexao();
conexao2.abrir();
dao.salvar(usuario, conexao2);
Temos a saída:
Primeiro exemplo
Conexão fechada, abrindo...
Invocando...
DAO: Executando SQL=[INSERT INTO USUARIOS VALUES(pcalcado)]
Fechando Conexão...
-------------------------
Segundo exemplo
Nenhuma conexão passada
-------------------------
Terceiro exemplo
Invocando...
DAO: Executando SQL=[INSERT INTO USUARIOS VALUES(pcalcado)]
Fechando Conexão...
Exercícios
1 – Modifique o exemplo tirando o log da classe GerenciadorArquivos e colocando esta funcionalidade
na classe PublicadorNoticias.
2 – Descubra quanto tempo os métodos demoram para executar utilizando advices. Primeiro tente com
Before e After, depois com Around.
Mais que isso, o Spring induz ao uso de boas práticas de programação ao se basear ostensivamente em
interfaces ao invés de classes. Esta característica, além de permitir extensão e adaptação fácil do
framework e ser um conceito da Orientação a Objetos, possibilita o uso de testes unitários com muita
facilidade.
Este objeto reúne estado e comportamento relativos ao conceito que queremos modelar em software, no
caso uma música. Ele mapeia da melhor maneira possível o conceito que entendemos como “música” para
objetos.
Ao utilizar EJBs, entretanto, teríamos que mapear este objeto de negócio em pelo menos dois objetos:
Utilizando POJOs
A Figura 6.1 traz um exemplo claro de POJO. É uma classe simples que representa um conceito de
negócio e não está presa à nenhum framework ou biblioteca.
POJO (Plain Old Java Object) é um objeto Java normal, que não implementa nem estende nenhuma classe
de infra-estrutura (de um framework, por exemplo). Um Servlet não é um POJO porque precisa estender
javax.servlet.http.HttpServlet para funcionar.
Idealmente o desenvolvimento OO em Java deve ser feito utilizando POJOs. Os conceitos da aplicação
devem ser implementados de forma extremamente simples, sem influência da arquitetura escolhida.
Como mencionado, esta abordagem possui problemas quando estamos utilizando J2EE. O maior deles é
que a infra-estrutura provida por este framework não foi pensada para o uso de objetos simples e sim de
objetos integrados ao framework como EJBs e Servlets.
Para não deixar que a infra-estrutura J2EE influencie diretamente sua aplicação e ainda assim obter as
funcionalidades que J2EE traz para os sistemas é preciso utilizar um chamado Container Leve.
Container Leve (Lightweight Container) é um container que dá a objetos Java normais (POJOs)
características que normalmente não estariam disponíveis para estes, como mecanismos de segurança,
transações e IoC.
Ao utilizar um container leve como o Spring é possível utilizarmos objetos simples como os da figura Figura
6.1 ao invés da estrutura “artificial” mostrada na Figura 6.2, exigida pelos EJBs.
Esta é a característica que distingue aplicações baseadas em containeres leves (como Spring)de outras
baseadas em herança e frameworks intrusivos (como EJB).
//métodos de negócio
}
E este seria o applicationConfig.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="livroDaoOracle"
class="br.com.fragmental.cursos.spring.apostila.capitulo6.LivroDaoOracle"
/>
<bean id="gerenciadorLivros"
class="br.com.fragmental.cursos.spring.apostila.capitulo6.GerenciadorLivro
s">
<property name="livroDao" ref="livroDaoOracle" />
</bean>
</beans>
Se quisermos que nosso sistema também possa utilizar MySQL, precisamos alterar o
GerenciadorLivros como mostrado no diagrama abaixo.
//métodos de negócio
}
Além da mudança no applicationContext.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="livroDaoOracle"
class="br.com.fragmental.cursos.spring.apostila.capitulo6.LivroDaoOracle"/
>
<bean id="livroDaoMySQL"
class="br.com.fragmental.cursos.spring.apostila.capitulo6.LivroDaoMySQL"/>
<bean id="gerenciadorLivros"
class="br.com.fragmental.cursos.spring.apostila.capitulo6.GerenciadorLivro
s">
<property name="livroDao" ref="livroDaoMySQL" />
</bean>
</beans>
O simples fato de isolar a lógica de acesso a dados no DAO permite que o GerenciadorLivros possa ser
alterado sem muita alteração de código, porém existe alteração no GerenciadorLivros e isso não é
necessário nem desejável.
Num sistema de verdade estas mudanças simples deveriam ser feitas em diversos lugares para cada
mudança deste tipo, e este não é o tipo de problema que ocorre apenas com mudanças drásticas como a
de o SGBD utilizado, ele se manifesta toda vez que alguma coisa na implementação mudar.
Pensando no problema, podemos abstrair o modelo chegando a conclusão que o GerenciadorLivros
precisa de um DAO para persistir os objetos. O problema que motiva a mudança de código no
GerenciadorLivros para mudarmos de banco de dados é que da forma como está implementado hoje
este não depende de um DAO, ele depende do LivroDaoOracle especificamente (a nossa mudança
apenas o fez depender de outro DAO). Esta é a dependência a ser eliminada.
Para eliminarmos a dependência direta, vamos fazer com que a classe GerenciadorLivros dependa de
fato de um DAO qualquer criando uma interface genérica para DAOs.
//métodos de negócio
}
E agora para alterar o DAO utilizado basta configurar o applicationContext.xml de conforme os
exemplos anteriores.
Este mesmo princípio deve ser utilizado ao relacionar os beans de negócio, não apenas de infra-estrutura.
Um bean deve depender do conceito representado por outro bean, não pela forma como ele está
implementado. Por exemplo, considere outro modelo de objetos.
Este é o mesmo problema que tivemos com o DAO anteriormente e podemos resolve-lo da mesma forma.
O GerenciadorCds não precisa especificamente do IdentificadorDeCdsCDDB, ele precisa de um
IndentificadorDeCDs qualquer. Isso nós podemos modelar com uma interface.
this.gerenciadorAutores = gerenciadorAutores;
}
// metodos de negocio
}
Configurados no applicationContext.xml desta forma:
<?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="gerenciadorAutores"
class="br.com.fragmental.cursos.spring.apostila.capitulo6.GerenciadorAutor
esImpl"/>
<bean id="gerenciadorEditoras"
class="br.com.fragmental.cursos.spring.apostila.capitulo6.GerenciadorEdito
rasImpl">
<property name="gerenciadorAutores" ref="gerenciadorAutores" />
</bean>
</beans>
Um Repository é onde os objetos ficam armazenados. Para as classes de negócio não importa se os
objetos são armazenados em arquivos XML, num banco de dados ou qualquer outro lugar, eles apenas
precisam que ao consultar o Repository possam obter estas classes. O Padrão Data Access Object – DAO
geralmente é utilizado para implementar o Repository. O Repository quase sempre é definido como uma
interface implementada pelo DAO.
Nota: A Sun publicou em seu Core J2EE Patterns um Padrão chamado Value Object. Este Padrão é uma
especialização do Padrão Data Transfer Object posteriormente catalogado por Martin Fowler. Na segunda
edição do livro a Sun alterou o nome deste para Transfer Object, porque Martin Fowler e Eric Evans já
haviam incluído um Padrão chamado Value Object (que é o descrito acima) em suas obras.
Geralmente programadores Java se referem ao Transfer Object como Value Object. Além do fato desta
nomenclatura estar desatualizada segundo o próprio catálogo de Padrões oficiais de Java EE, Transfer
Objects não devem ser utilizados em sistemas que utilizam apenas uma JVM. Um TO utilizado desta
maneira acaba com a Orientação a Objetos criando um design altamente Procedural. Este Padrão foi
publicado visando uso em sistemas onde Entity Beans trocam informações numa rede.
} catch (MalformedURLException e) {
throw new IllegalStateException("Problemas ao gerar URL da
foto", e);
}
return url;
}
}
O que os Services fazem é apenas coordenar o fluxo de interações. Vamos ver a implementação da criação
de um novo usuário:
public class GerenciadorUsuariosImpl implements GerenciadorUsuarios {
private GerenciadorFotos gerenciadorFotos = null;
private UsuarioRepository usuarioRepository=null;
public void setUsuarioRepository(UsuarioRepository usuarioRepository) {
this.usuarioRepository = usuarioRepository;
}
public Usuario novoUsuario(String login, File foto){
Usuario novoUsuario = new Usuario(login);
Foto fotoUsuario = gerenciadorFotos.enviarFoto(login, foto);
novoUsuario.setFoto(fotoUsuario);
usuarioRepository.salvar(novoUsuario);
return novoUsuario;
}
public void setGerenciadorFotos(GerenciadorFotos gerenciadorFotos) {
this.gerenciadorFotos = gerenciadorFotos;
}
}
fotoRepository.salvar(foto);
return foto;
}
Por último, os Repositories são implementados por DAOs que, neste exemplo fictício, apenas escrevem na
tela:
public class UsuarioDao implements UsuarioRepository{
public void salvar(Usuario usuario) {
System.out.println("SALVANDO USUARIO ["+usuario.getLogin()+"]");
}
}
<bean id="gerenciadorFotos"
class="br.com.fragmental.cursos.spring.apostila.capitulo6.exemplo.GerenciadorFot
osImpl">
<property name="fotoRepository" ref="fotoRepository" />
</bean>
<bean id="gerenciadorUsuarios"
class="br.com.fragmental.cursos.spring.apostila.capitulo6.exemplo.GerenciadorUsu
ariosImpl">
<property name="gerenciadorFotos" ref="gerenciadorFotos" />
<property name="usuarioRepository" ref="usuarioRepository" />
</bean>
</beans>
Exercícios
1 – No modelo apresentado na seção sobre Padrões de Modelagem de Domínio, implemente a
funcionalidade que permite aos usuários:
a) Indicarem e removerem seus contatos
b) Enviarem e removerem fotos
c) Alterar sua foto de usuário
Bibliografia
6 & '$ , 0 ,$ 6= !
& ' ## ) #( > # 1 '&'? @ ,A 0 ,
B ; , C
D; ,6 D ; , , $E , F6 = G$ +H I=""=%="%