Documente Academic
Documente Profesional
Documente Cultură
0)
Framework para
Desenvolvimento de Aplicações
em Java
Diego Pacheco
Dez/2007
Diego Pacheco
Sumário
BeanFactoryPostProcessors ............................................................................................3-21
Property Editors .................................................................................................................3-25
Eventos ....................................................................................................................................3-30
PropertyPlaceholderConfigurer .................................................................................3-33
SingletonBeanFactoryLocator ......................................................................................3-36
Internacionalização .........................................................................................................3-40
Exercicios ..............................................................................................................................3-44
Espaço para anotações ....................................................................................................3-45
1. Introdução
Objetivos
• Conhecer os conceitos básicos;
• Conhecer os principais cenários de uso;
• Prover uma visão de todo o portifólio do Spring;
• Saber onde baixar o Spring;
• Conhecer a estrutura de diretórios dos fontes.
Conceitos Básicos
Esse framework foi muito bem dividido, e isso pode ser observado com
clareza na figura a baixo.
• É melhor programar para interfaces do que para classes, Spring reduz o custo
e a complexidade de usar interfaces para zero.
Integração com Toplink, Hibernate, JDO, and iBATIS SQL Maps: Em termos
de resource holders, suporte a implementações de DAOs e estratégias transacionais.
Existe um suporte de primeira classe para Hibernate com muitas facilidades
providas pelo mecanismo de IoC.
Cenários de uso
Podemos utilizar Spring nos mais diversos cenários, desde um simples applet
até mesmo nas mais complexas aplicações corporativas. Um exemplo típico de uso
do Spring em uma aplicação completa Java EE onde teremos:
No caso de sua aplicação precisar fazer algum acesso remoto o Spring prove
isso de maneira transparente e elegante.
Podemos usar Spring também se for necessário fazer integração com EJB, é
possível utilizar os recursos da camada de acesso e abstração de EJB.
Portifólio
Spring IDE para eclipse: Plugin para o ide eclipse com facilidades para o uso
de Spring. Preove auto complete para os xmls de configuração do Spring, módulo
visual para o Spring Web Flow, negação entre os beans do Spring, e visualização de
recursos AOP.
Spring Batch: Prove suporte para execução de tarefas muito longas. Suporte
a programas batch os quais processam um volume muito grande de informações de
um banco de dados. Com suporte a agendamento automático ou manual após
falhas, esse projeto promove suporte de execuções batch para ambientes
corporativos.
Download
O Spring Framework pode ser obtido através do site:
http://www.springframework.org/download existe a possibilidade de baixar
somente o framework ou baixar o framework e suas dependências. A Versão do
Spring que será usada nessa apostila é a 2.0.6 , mas muitos itens descritos aqui são
válido também para a versão 1.2.x do framework.
Estrutura de Diretórios
aspectj: Fontes dos testes dos artefatos que utilizam aspectj, aqui temos
alguns artefatos de transação feitos em aspectJ.
dist: Distribuição binárias dos fontes do Spring, aqui você encontrará os jars
do Spring, bem como todos os jars de todos os módulos separados.
src: Contém todos os fontes do Spring framework, caso você precise desses
fontes para debugar o comportamento de algum código do Spring.
test: Nesse diretório você encontrará todos os fontes dos testes realizados
com o Spring, é útil para aprender como utilizar algumas classes do Spring.
tiger: Todos os fontes que utilizam recursos somente do Java 5.0 estão nesse
diretório, como por exemplo, annotations.
Dois arquivos que estão soltos no diretório principal e são interessantes são
o changelog.txt e o readme.txt. No arquivo changelog contém todas as mudanças
dessa versão do Spring como, por exemplo: quais são as novas features e quais
foram os bugs corrigidos. No arquivo readme nós temos a definição de cada
modulo do Spring com suas dependências, isso é muito útil se você deseja utilizar
somente alguns módulos do Spring e precisa saber as dependências.
Exercícios
2. Container IoC
Objetivos
São dois tipos de injeção que o Spring utiliza em seu container de Ioc, veja:
• Desacoplamento
Para fixar melhor esse conceito considere o seguinte exemplo pratico: Imagine
que um Autor escreveu muitos livros, então considere os seguintes pojos:
package com.targettrust.spring.bad;
import java.util.List;
public Autor() {}
package com.targettrust.spring.bad;
public Livro() {}
@Override
public String toString() {
return "titulo: " + titulo + " editor: " + editora +
" ano: " + ano;
}
}
Código 2.2 Livro.java
package com.targettrust.spring.bad;
import java.util.ArrayList;
import java.util.List;
for(Livro l: livros){
if (l.getAutor().getNome().equals(nome))
achados.add(l);
}
return achados;
}
package com.targettrust.spring.bad;
autor.setNome("Diego Pacheco");
autor.listarPorNome();
}
}
Código 2.4 MainTest.java
Nós temos os seguintes objetos: Autor, Livro, Listar e MainTest. Nesse exemplo
um Autor pode ter vários livros por isso dentro do Pojo de Autor temos List<Livro>
e a classe Listar se encarrega de armazenar os livros e autores e prover uma procura
sobre esses dados. Como vocês devem ter percebido o pojo Autor contém um
método chamado: listarPorNome que instância esse objeto Listar e procura pelos
livros do autor. Esse exemplo por mais simplório, serve para demonstrar que existe
um forte acoplamento entre o pojo Autor e a classe que lista os Livros por Autor,
outro grande problema nesse exemplo é que não existem interfaces. O que
aconteceria se um Autor pudesse ter outras coisas além de livros como, por exemplo:
artigos, co-autorias, vídeos? Nesse caso essa mudança iria implicar em um refactoring
muito grande nessas classes, mas como poderíamos resolver esse problema?
package com.targettrust.spring.bad.ok;
import java.util.List;
public Autor() {}
package com.targettrust.spring.bad.ok;
public Livro() {}
@Override
public String getNome() {
return getTitulo();
}
@Override
public String getTipo() {
return "Livro";
}
@Override
}
Código 2.6 Livro.java
package com.targettrust.spring.bad.ok;
package com.targettrust.spring.bad.ok;
import java.util.List;
package com.targettrust.spring.bad.ok;
import java.util.ArrayList;
import java.util.List;
for(Publicavel p: publicaveis){
if (p.getAutor().equals(nome))
achados.add(p);
}
return achados;
}
package com.targettrust.spring.bad.ok;
autor.setNome("Rod");
autor.listarPorNome();
autor.setNome("Diego Pacheco");
autor.listarPorNome();
}
Registrando Beans
Agora vamos ver como registrar essas classes no contexto do Spring. Esse
registro é muito simples, ele consiste em apontar para uma classe Java no seu
classpath e dar um id a esse bean. Existem outras configurações que podemos fazer
sobre esses beans, mas vamos começar mostrando como declarar um bean no
Spring.
</beans>
package com.targettrust.spring.primeiro;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
package com.targettrust.spring.singleton;
import java.util.ArrayList;
import java.util.List;
public class Uf {
public Uf() {
System.out.println("Inicio do processamento dos estados...");
initUfs();
}
ufs.add(new Uf("Rorâima","RR"));
ufs.add(new Uf("São Paulo","SP"));
ufs.add(new Uf("Santa Catarina","SC"));
ufs.add(new Uf("Sergipe","SE"));
ufs.add(new Uf("Tocantins","TO"));
}
</beans>
package com.targettrust.spring.singleton;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
Uf bean1 = (Uf)bf.getBean("uf");
System.out.println("ufs: " + bean1.getUfsBrazil());
bean1.showInstance();
Uf bean2 = (Uf)bf.getBean("uf");
System.out.println("ufs: " + bean2.getUfsBrazil());
bean2.showInstance();
Uf bean3 = (Uf)bf.getBean("uf");
System.out.println("ufs: " + bean3.getUfsBrazil());
bean3.showInstance();
}
}
Perceba que a criação dos estados ocorre somente na primeira vez, mas nas
outras vezes já está em memória, logo além, se não fizer duas vezes o mesmo
processamento, existe um ganho de performance.
</beans>
Lazy Initialization
Outro recurso importante é o Lazy Initialization, com ele podemos fazer com
que o Spring só carregue os beans que forem solicitados, ou seja, se temos 50 beans
declarados no contexto do Spring e apenas 10 são utilizados, os outros 40 beans não
vão ser instanciados, e assim evitamos um processamento desnecessário e ganhamos
em desempenho.
<bean
id="uf"
class="com.targettrust.spring.singleton.Uf"
scope="prototype"
lazy-init="true"
/>
</beans>
</beans>
Código XML 2.4 Spring-beans.xml um exemplo de default lazy initialization.
Scopo Descrição
package com.targettrust.spring.scope;
public SimpleBean() {}
@Override
public String toString() {
return super.toString() + " id: " + id;
}
}
<bean
id="beanB"
class="com.targettrust.spring.scope.SimpleBean"
scope="prototype"
/>
</beans>
package com.targettrust.spring.scope;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
BeanFactory bf =
new
ClassPathXmlApplicationContext("/com/targettrust/spring/scope/Spring-
beans.xml");
}
Código 2.18 Teste de Scopes.
package com.targettrust.spring.scope.myscope;
public Pessoa() {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((nome == null) ? 0 :
nome.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
return (obj == null) ? false : nome.equals(((Pessoa)
obj).getNome());
}
@Override
public String toString() {
return "nome: " + nome;
}
}
Código 2.19 Pessoa.java.
package com.targettrust.spring.scope.myscope;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SuppressWarnings("unused")
public class TestScopes {
Pessoa p1 = (Pessoa)bf.getBean("pessoa1");
Pessoa p2 = (Pessoa)bf.getBean("pessoa2");
Pessoa p3 = (Pessoa)bf.getBean("pessoa3");
Pessoa p4 = (Pessoa)bf.getBean("pessoa4");
System.out.println("Todas as pessoas: " +
ThreadLocalScope.tl.get());
p3.setNome("Spider-Pig");
System.out.println("Todas as pessoas: " +
ThreadLocalScope.tl.get());
}
}
Código 2.20 Classe de testesTestScopes.java.
<bean id="pessoa1"
class="com.targettrust.spring.scope.myscope.Pessoa"
scope="threadLocal"
>
<property name="nome" value="Diego Pacheco"/>
</bean>
<bean id="pessoa2"
class="com.targettrust.spring.scope.myscope.Pessoa"
scope="threadLocal"
>
<property name="nome" value="Rod Johnson"/>
</bean>
<bean id="pessoa3"
class="com.targettrust.spring.scope.myscope.Pessoa"
scope="threadLocal"
>
<property name="nome" value="Juergen Hoeller"/>
</bean>
<bean id="pessoa4"
class="com.targettrust.spring.scope.myscope.Pessoa"
scope="singleton"
>
<property name="nome" value="Ninguem me viu!!!"/>
</bean>
</beans>
Código XML 2.6 Spring-beans.xml scope customizado.
package com.targettrust.spring.scope.myscope;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public ThreadLocalScope() {
tl = new ThreadLocal<Map<String,Object>>();
tl.set(new HashMap<String, Object>());
}
@Override
public Object get(String name, ObjectFactory objectFactory) {
synchronized(tl){
@Override
public Object remove(String name) {
synchronized(tl){
Object obj = tl.get().remove(name);
return obj;
}
}
@Override
public void registerDestructionCallback(String name, Runnable
callback){
throw new UnsupportedOperationException("Essa operação de
registerDestructionCallback, não suportada!");
}
@Override
public String getConversationId() {
return null;
}
}
<bean
class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="threadLocal">
<bean
class="com.targettrust.spring.scope.myscope.ThreadLocalScope"/>
</entry>
</map>
</property>
</bean>
Código XML 2.7 Definição de scope customizado.
Essa é uma das duas formas que o Spring faz injeção de dependências, ele
utiliza um método setter conforme padrão da Sun e através desse método setta os
valores no objeto em questão.
package com.targettrust.spring.setter;
public Aluno() {}
@Override
public String toString() {
return "nome: " + nome +
" idade: " + idade +
" desconto: " + desconto +
" sexo: " + sexo;
}
}
Código 2.22 Aluno.java.
package com.targettrust.spring.setter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
System.out.println(bf.getBean("aluno"));
}
}
Não existe nada de mágico na injeção via setter, é simples, basta ter o método
setter e o tipo de dado injetado ser o mesmo.
package com.targettrust.spring.constructor;
@Override
public String toString() {
return " nome: " + nome +
" idade: " + idade +
" morotista: " + cartaMorotista;
}
}
Código 2.24 Pessoa.java
package com.targettrust.spring.constructor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
BeanFactory bf =
new
ClassPathXmlApplicationContext("/com/targettrust/spring/constructor/Spring
-beans.xml");
System.out.println(bf.getBean("pessoa"));
}
}
Código 2.25 TestConstructor.java Teste de construtor
Caso você tenha um construtor que receba uma String e um número, o Spring
pode acabar sentando valores indesejados, para resolver esse tipo de situação,
podemos especificar o tipo do argumento do construtor, conforme exemplo de XML
abaixo.
<bean
id="animal"
class="com.targettrust.spring.constructor.Animal"
>
<constructor-arg index="1" value="Black" />
<constructor-arg index="0" value="Dog" />
</bean>
Código XML 2.11 Exemplo de tipo de argumento para construtor com index.
Injeção de coleções
Por deafult o Spring possui tags específicas para injeção de coleções, é possível
injetar: Map, List, Properties e Set. De fato podemos injetar qualquer Collection, mas
para injetar uma coleção que não seja uma das citadas, será necessário usar um
Custon Property Editor. Para fazer as injeções das coleções “nativas” do Spring,
utilizamos as tags: <map/>, <list/>, <set/> e <props/>. Considerando o seguinte
exemplo:
>
<property name="pessoas">
<set>
<value>Diego</value>
<value>Rod</value>
<value>Alef</value>
</set>
</property>
<property name="cadeiras">
<map>
<entry>
<key><value>1</value></key>
<value>diego</value>
</entry>
<entry>
<key><value>2</value></key>
<value>Rod</value>
</entry>
</map>
</property>
<property name="vededoresPipoca">
<list>
<value>Ze</value>
<value>JoZe</value>
<value>MaisEh</value>
</list>
</property>
<property name="detalhes">
<value>
estadio.luz=forte
estadio.taman.hq.full=grande
estadio.fundac.since=10/10/2001
estadio.status.now=ativo
</value>
</property>
</bean>
</beans>
Código XML 2.12 Exemplo de injeção de collections.
package com.targettrust.spring.collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public Estadio() {}
@Override
public String toString() {
return " pessoas: " + pessoas + "\n" +
" cadeiras: " + cadeiras + "\n" +
" Vededores de Pipoca: " + vededoresPipoca + "\n" +
" detalhes: " + detalhes;
}
}
package com.targettrust.spring.collection;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
<property name="detalhes">
<props>
<prop key="estadio.luz">forte</prop>
<prop key="estadio.taman.hq.full">grande</prop>
<prop key="estadio.fundac.since">10/10/2001</prop>
<prop key="estadio.status.now">ativo</prop>
</props>
</property>
Código XML 2.13 Outra forma de injeção de Properties.
Dica: Se for explicitamente necessário injetar uma coleção nula, ou settar null em
alguma propriedade de algum bean do Spring, podemos utilizar a tag <null/>.
package com.targettrust.spring.collection;
import java.util.LinkedList;
public Cidade() {}
<bean
id="cidade"
class="com.targettrust.spring.collection.Cidade"
>
<property name="ruas">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
</bean>
</beans>
Código XML 2.14 Uso de PropertyEditors.
No Spring são chamados de beans colaboradores todo objeto que é criado por
você, como por exemplo, um objeto de negócio e que deve ser injetado em outro
objeto, em síntese é o ato de injetar um pojo em outro. Não existe nada de mágico
nisso, além de simples é muito usual, talvez a funcionalidade mais simples do
container IoC do Spring, porém a mais utilizada junto com as injeções por setter. A
injeção de colaboradores é feita através de setters ou construtor como as outras
injeções vistas anteriormente.
package com.targettrust.spring.colaboradores;
public Cidade() {}
@Override
public String toString() {
return nome;
}
}
Código 2.28 Cidade.java Classe colaboradora.
package com.targettrust.spring.colaboradores;
import java.util.List;
public Estado() {}
@Override
public String toString() {
return sigla + " cidades: " + cidades;
}
}
>
<property name="nome"><value>Porto Alegre</value></property>
</bean>
<bean
id="cidB"
class="com.targettrust.spring.colaboradores.Cidade"
>
<property name="nome"><value>Gravataí</value></property>
</bean>
<bean
id="estado"
class="com.targettrust.spring.colaboradores.Estado"
>
<property name="sigla" value="RS" />
<property name="cidades">
<list>
<ref bean="cidA" />
<ref local="cidB" />
<bean
class="com.targettrust.spring.colaboradores.Cidade">
<property name="nome" value="Canoas" />
</bean>
</list>
</property>
</bean>
</beans>
Código XML 2.15 Injeções de colaboradores.
package com.targettrust.spring.colaboradores;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
Código XML 2.16 web.xml.
Agora é só criar um bean no Spring para podermos utilizar esse contexto web.
Nesse exemplo será criado o DateService que é um service que irá prover a data
atual. Observe o código abaixo:
package com.targettrust.spring.web;
/>
</beans>
Código XML 2.17 Spring-beans.xml.
Para testar, vamos empacotar essa aplicação em um arquivo war. Para fazer
esse teste vamos chamar o serviço do Spring através de uma página jsp. Confira o
código abaixo:
<html>
<title>Spring WEB</title>
<body>
<center><h2>Exemplo de Spring com WEB-Tier</h2></center><br>
A Data atual é:
<%=((DateService)WebApplicationContextUtils.getWebApplicationContext(getSe
rvletConfig().getServletContext()).getBean("dateService")).getDate()%>
</body>
</html>
Código 2.23 index.jsp.
Exercícios
3. Manipulação de Beans
Objetivos
Resource Loaders
/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml
Código de exemplos wild cards
Init-Metohd e InitializingBean
O Spring prove alguns mecanismos de call back após a inicialização de um
bean. Após o container de IoC do Spring resolver as injeções de dependências ele
prove um Call Back, ou seja, ele pode estar invocando um método em sua classe e a
partir desse método você realiza algum processamento. Essa operação pode ser
configurada através da propriedade init-method. Essa solução é elegante, pois não
gera acoplamento entre o código do Spring e seu código. Veja isso na prática no
exemplo abaixo:
package com.targettrust.spring.init;
import java.util.Date;
public HoraCertaService() {
System.out.println("Criando Bean de HoraCertaService ");
}
@SuppressWarnings("deprecation")
public String getHoraCerta(){
return pais + " -> " + data.getHours() + ":" +
data.getMinutes() + ":" + data.getSeconds();
}
</beans>
Código XML 3.2 injeção com uso de init-method
package com.targettrust.spring.init;
import org.springframework.beans.factory.BeanFactory;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
HoraCertaService bean =
(HoraCertaService)bf.getBean("horaCertaService");
System.out.println("Hora: " + bean.getHoraCerta());
}
}
Código 3.2 Testjava
Herança de Definições
package com.targettrust.spring.extend;
public PessoaService() {}
package com.targettrust.spring.extend;
public PessoaFisicaService() {}
value="pacheco@diego.com.spring" />
<property name="telefone" value="455-55-55" />
</bean>
<bean
abstract="false"
id="pessoaFisicaService"
class="com.targettrust.spring.extend.PessoaFisicaService"
parent="pessoaService"
>
<property name="cpf" value="47888971210" />
</bean>
</beans>
Código XML 3.3 injeção com uso de herança.
package com.targettrust.spring.extend;
import org.springframework.beans.factory.BeanFactory;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
PessoaFisicaService bean =
(PessoaFisicaService)bf.getBean("pessoaFisicaService");
bean.mostraPessoa();
}
}
Código 3.5 Teste.java
Validação
package com.targettrust.spring.validate;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
public Pessoa() {}
@Override
@SuppressWarnings("unchecked")
public boolean supports(Class clazz) {
return Pessoa.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "nome", "nome.vazio");
Pessoa p = (Pessoa) target;
if (p.getIdade() < 0) {
errors.rejectValue("idade", "valor negativo");
} else if (p.getIdade() > 120) {
errors.rejectValue("idade", "velho demais");
}
}
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
}
Código 3.6 Pessoa.java e classe de validação
>
<property name="nome" value="Fulano" />
<property name="idade" value="-1" />
</bean>
</beans>
Código XML 3.4 injeção com uso de validator
package com.targettrust.spring.validate;
import org.springframework.beans.factory.BeanFactory;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
"/com/targettrust/spring/validate/Spring-beans.xml");
}
}
Código 3.7 Teste.java
Bean Wrapper
Bean Wrapper é um recurso do Spring que funciona com Java beans como o
próprio nome já diz, Java beans é um especificação da Sun que define se um
determinado objeto deve ter um construtor vazio e métodos getters e setters de
acordo com o padrão da Sun camelCase. Essa funcionalidade é na verdade um
pattern do GOF que se chama decorator. Que tambem é conhecido como Wrapper
que significa empacotar, no caso nós temos uma camada de adiciona um
funcionalidade adicional a algo existente.
Esse recurso pode ser utilizado para fazer manipulações com propriedades
de objetos de maneira elegante. Também é usual para fazer algum tipo de bind em
sua aplicação. Confira como codificar abaixo:
package com.targettrust.spring.beanwrapper;
public Funcionario() {}
@Override
public String toString() {
return "Funcionario[ nome: " + nome +
" , salario: " + salario + " ]";
}
Código 3.8 Funcionario.java
package com.targettrust.spring.beanwrapper;
public Empresa() {}
@Override
public String toString() {
return "Empresa [ " + nome + " | " + gestor.toString() + "
]";
}
}
Código 3.9 Empresa.java
>
<property name="nome" value="João" />
<property name="salario" value="100" />
</bean>
<bean
id="empresa"
class="com.targettrust.spring.beanwrapper.Empresa"
>
<property name="nome" value="CromFactory" />
<property name="gestor" ref="gestor" />
</bean>
</beans>
Código XML 3.5
package com.targettrust.spring.beanwrapper;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
System.out.println(bf.getBean("empresa"));
company.setPropertyValue("gestor",
diego.getWrappedInstance());
System.out.println(company);
System.out.println(company.getWrappedInstance());
System.out.println(((BeanWrapperImpl)company).getPropertyDescriptor
("gestor.salario").getPropertyType());
diego.setPropertyValue("salario",200L);
System.out.println(diego.getPropertyValue("salario"));
}
}
Código 3.10 Teste.java
Expressão Descrição
nome Corressponde a propriedade nome do objeto empacotado, irá
tentar executar um getNome() ou isNome()
conta.valor Indica que no objeto corrente existe uma propriedade conta
que tem uma propriedade valor, executa algo como:
getConta().getValor()
filhos[2] Acessa a segunda posição da propriedade filhos sendo que
filhos pode ser um array ou List ou qualquer outra collection
ordenada.
estado[poa] Indica que está sendo acessado a entrada poa em uma Map
chamado estado.
BeanPostProcessors
Esse tipo de Registro não é conveniente, por causa disso que para muitas
aplicações as pessoas preferem utilizar ApplicationContex ao invés de BeanFactory.
Classes que implementam BeanPostProcessor são classes especiais, logo são
tratadas de maneira diferente pelo container. Todas as BenPostProcessors e aqueles
beans que estejam diretamente referenciados por eles serão instanciados ao startup
do Spring.
Os recursos de AOP auto-proxing são implementados como
BeanPostProcessor, nenhum BeanPostProcessor ou beans diretamente
referenciados serão elegíveis para auto-proxy, ou seja, não haverá aspectos sobre
eles. O Spring irá lhe alertar sobre essa situação com uma mensagem semelhante a
essa: “Bean 'foo' is not eligible for getting processed by all BeanPostProcessors (for
example: not eligible for auto-proxying)”. Veja um exemplo prático de como utilizar
este recurso:
package com.targettrust.spring.beanpostprocessors;
}
Código 3.12 ObjetoA.java
package com.targettrust.spring.beanpostprocessors;
}
Código 3.13 ObjetoB.java
package com.targettrust.spring.beanpostprocessors;
}
Código 3.14 ObjetoC.java
package com.targettrust.spring.beanpostprocessors;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
@Override
public Object postProcessBeforeInitialization(Object bean, String
beanName)
throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String
beanName)
throws BeansException {
System.out.println("Craindo bean: " + beanName);
return bean;
}
}
Código 3.15 LogCreationBeanPostProcessor.java
package com.targettrust.spring.beanpostprocessors;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
BeanFactoryPostProcessors
Similar ao BeanPostProcessor, porém o BeanFactoryPostProcessor consegue
ler os metadados de configuração do Spring e também mudar esses dados antes
que o Spring crie seus beans. Você pode configurar muitos
BenFactoryPostProcessor. É possível configurar a ordem de execução dessa classe
implementando a interface Ordered. Se você deseja mudar a instância de um bean
já criado você deve utilizar o BeanPostProcessor conforme apresentando no tópico
anterior. O Próprio Spring utiliza beanFactoryPostProcessor como:
PropertyResourceConfigurer e PropertyPlaceholderConfigurer.
Se estamos utilizando ApplicationContext o registro é feito de forma
automática, se estivermos utilizando uma BeanFactory precisamos fazer isso de
forma manual. A Forma de fazer isso está exemplificada logo abaixo:
cfg.postProcessBeanFactory(factory);
Código 3.17 Exemplo de registro de BeanPostProcessor.
package com.targettrust.spring.beanfactorypostprocessors;
import java.text.SimpleDateFormat;
import java.util.Date;
}
}
Código 3.18 DataService.java
package com.targettrust.spring.beanfactorypostprocessors;
import java.io.FileInputStream;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@Override
@SuppressWarnings("unchecked")
public void postProcessBeanFactory(ConfigurableListableBeanFactory
beanFactory) throws BeansException {
Map<Object,Object> beans =
beanFactory.getBeansOfType(DataService.class);
for(Entry<Object, Object> e: beans.entrySet()){
System.out.println("Aplicando pattern em service: " +
e);
DataService service = ((DataService)e.getValue());
service.setPattern(p.getProperty("pattern." +
service.getPattern().replace("#", "")));
}
}
}
Código 3.19 DatePatternRouterBeanFactoryPostProcessor.java
class="com.targettrust.spring.beanfactorypostprocessors.DataService"
>
<property name="pattern" value="#brasil" />
</bean>
<bean
id="usDateService"
class="com.targettrust.spring.beanfactorypostprocessors.DataService"
>
<property name="pattern" value="#us" />
</bean>
<bean
class="com.targettrust.spring.beanfactorypostprocessors.DatePatternR
outerBeanFactoryPostProcessor" />
</beans>
Código XML 3.7 Xml de configurações
package com.targettrust.spring.beanfactorypostprocessors;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
Property Editors
<bean
id="txt"
class="java.io.File"
>
<constructor-arg index="0" value="C:/BOOTSECT.BAK" />
</bean>
Após isso seria possível injetar esse bean txt no bean que necessita de um
File. Porém isso é muito doloroso e nada produtivo. Não seria mais fácil se o Spring
detectar-se automaticamente uma propriedade de um bean que é um File e criasse
o File para nós? O Spring já faz isso para nós se injetarmos um caminho para uma
File ele automaticamente faz isso para nós, por que ele já tem um PropertyEditor
default que está registrado para tal trabalho. Esse Property Editor default chama-se
FileEditor. Confira os PropertyEditor disponíveis no Spring:
com
chank
pop
Foo
FooEditor // PropertyEditor para a Classe Foo
Você pode criar seu próprio Property Editor, para isso você precisará
estender a classe PropertyEditorSupport e registrar esse custom editor criado por
você no XML do Spring. Veja o exemplo a seguir:
package com.targettrust.spring.propertyeditor;
public Aluno() {}
@Override
public String toString() {
return nome;
}
}
Código 3.21 Aluno.java
package com.targettrust.spring.propertyeditor;
import java.util.List;
public Turma() {}
@Override
public String toString() {
return nome + " alunos: " + alunos.toString();
}
}
Código 3.22 Turma.java
package com.targettrust.spring.propertyeditor;
import java.beans.PropertyEditorSupport;
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-2.0.xsd">
<bean id="turma"
class="com.targettrust.spring.propertyeditor.Turma">
<property name="nome" value="Curso-Spring" />
<property name="alunos">
<list>
<value>Rod</value>
<value>Joe</value>
<value>Bart</value>
<value>Homer</value>
<value>Hammer</value>
</list>
</property>
</bean>
<bean id="customEditorConfigurer"
class="org.springframework.beans.factory.config.CustomEditorConfigu
rer">
<property name="customEditors">
<map>
<entry
key="com.targettrust.spring.propertyeditor.Aluno">
<bean
class="com.targettrust.spring.propertyeditor.AlunoEditor" />
</entry>
</map>
</property>
</bean>
</beans>
Eventos
A escuta de eventos em um objeto ApplicationContext é feita através dos
objetos ApplicationEvent e ApplicationListener. Se um bean implementa a interface
ApplicationListener é publicado no contexto do Spring toda vez que for publicado
um evento do tipo ApplicationEvent o seu bean vai ser notificado. Confira alguns
dos eventos disponíveis por padrão na ApplicationContext.
Evento Descrição
package com.targettrust.spring.event;
import org.springframework.context.ApplicationEvent;
package com.targettrust.spring.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
package com.targettrust.spring.event;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@Override
public void setApplicationContext(ApplicationContext
applicationContext)
throws BeansException {
ac = applicationContext;
}
}
Código 3.26 RH.java
/>
<bean
id="funcionarioEsperto"
class="com.targettrust.spring.event.FeriadoListener"
/>
</beans>
Código XML 3.10 Registro do listener
package com.targettrust.spring.event;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
RH rh = (RH)ac.getBean("rh");
rh.pulicarFeriados();
}
}
Código 3.27 Teste.java
Nesse exemplo foi utilizado o FeridoEvent que é uma classe que estende
ApplicationEvent do Spring. Para tratar o evento foi criada a classe FeriadoListener
que implementa ApplicationListener com um if parta saber se o evento disparado foi
o FeriadoEvent. A Classe RH é uma ApplicationContextAware para poder publicar o
evento através do método publishEvent.
PropertyPlaceholderConfigurer
package com.targettrust.spring.propertyplaceholderconfigurer;
public Instrutor() {}
}
public void setIdade(int idade) {
this.idade = idade;
}
@Override
public String toString() {
return " nome: " + nome +
" idade: " + idade +
" altura : " + altura+
" time: " + time ;
}
}
Código 3.28 Instrutor.java
nome=deigo
idade=22
altura=1.85
Código 3.28 config.properties
class="org.springframework.beans.factory.config.PropertyPlaceholder
Configurer">
<property name="location">
<value>classpath:com/targettrust/spring/propertyplaceholderconfigur
er/config.properties</value>
</property>
<property name="properties">
<value>time=Grêmio</value>
</property>
</bean>
<bean
id="i"
class="com.targettrust.spring.propertyplaceholderconfigurer.Instrut
or"
>
<property name="nome" value="${nome}" />
<property name="idade" value="${idade}" />
<property name="time" value="${time}" />
<property name="altura" value="${altura}" />
</bean>
</beans>
Código XML 3.11 Registro do PropertyPlaceHolderConfigurer
package com.targettrust.spring.propertyplaceholderconfigurer;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
"/com/targettrust/spring/propertyplaceholderconfigurer/Spring-
beans.xml");
Instrutor i = (Instrutor)ac.getBean("i");
System.out.println("Instrutor: " + i);
}
}
Código 3.29 Teste.java
nomeBean.propriedade=valor
outronomeBean.outrapropriedade=outrovalor
maisumnomeBean.maisumapropriedade=maisumvalor
Código 3.29 config.properties para PropertyOverrideConfigurer
SingletonBeanFactoryLocator
package com.targettrust.spring.singletonbeanfactorylocator;
/>
</beans>
Código XML 3.12 Spring-beans-A.xml
default-lazy-init="true"
>
<bean
id="A2"
class="com.targettrust.spring.singletonbeanfactorylocator.ObjetoA"
/>
</beans>
Código XML 3.13 Spring-beans-B.xml
/>
</beans>
Código XML 3.13 Spring-beans-C.xml
/>
</beans>
Código XML 3.14 Spring-beans-D.xml
<bean
id="beanFactoryBean"
class="org.springframework.context.support.ClassPathXmlApplicationC
ontext"
>
<constructor-arg>
<list>
<value>com/targettrust/spring/singletonbeanfactorylocator/Spring-
beans-A.xml</value>
<value>com/targettrust/spring/singletonbeanfactorylocator/Spring-
beans-B.xml</value>
<value>com/targettrust/spring/singletonbeanfactorylocator/Spring-
beans-C.xml</value>
</list>
</constructor-arg>
</bean>
<bean
id="beanFactoryBean2"
class="org.springframework.context.support.ClassPathXmlApplicationC
ontext"
>
<constructor-arg>
<list>
<value>com/targettrust/spring/singletonbeanfactorylocator/Spring-
beans-D.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
Código XML 3.15 beanRefFactory.xml.xml
package com.targettrust.spring.singletonbeanfactorylocator;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import
org.springframework.beans.factory.access.SingletonBeanFactoryLocator;
ObjetoA a1 = (ObjetoA)bf.getFactory().getBean("A1");
ObjetoA a2 = (ObjetoA)bf.getFactory().getBean("A2");
ObjetoA a3 = (ObjetoA)bf.getFactory().getBean("A3");
bf = bfl.useBeanFactory("beanFactoryBean2");
ObjetoA a4 = (ObjetoA)bf.getFactory().getBean("A4");
}
}
Código 3.30 Teste.java
Internacionalização
package com.targettrust.spring.i18n;
import java.util.Locale;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
public Pessoa() {}
@Override
public void setMessageSource(MessageSource messageSource) {
ms = messageSource;
}
@Override
public String toString() {
return "nome: " + nome + " idade: " + idade;
}
}
Código 3.31 Pessoa.java
class="org.springframework.context.support.ResourceBundleMessageSou
rce">
<property name="basenames">
<list>
<value>mensagems</value>
</list>
</property>
</bean>
<bean
id="pessoa"
class="com.targettrust.spring.i18n.Pessoa"
/>
</beans>
Código XML 3.16 Spring-beans.xml
package com.targettrust.spring.i18n;
import java.util.Locale;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
Locale.setDefault(Locale.ENGLISH);
setIdade(ac);
Locale.setDefault(new Locale("pt","BR"));
setIdade(ac);
try{
p.setIdade(200);
}catch(RuntimeException re){
re.printStackTrace();
}
}
}
Código 3.34 Teste.java
Exercicios
4. Persistência
Objetivos
Hierarquia de Exceptions
O Spring framework provê uma camada de acesso a dados chamada DAO support
que prove diversas facilidades para trabalhar com JDBC, Hibernate, JDO, JPA. Esse recurso
permite trocar a tecnologia de persistência com mais facilidade e menos esforço, sem a
necessidade de se preocupar com as execptions que variam de tecnologia para tecnologia.
O Spring tem sua própria árvore de exceptions traduzindo as exceptions de tecnologias
específicas como SQLException para sua própria árvore de exceptions, onde sua exception
raiz é a DataAccessException. Esses exceptions empacotam a exception raiz, assim você
não perde nenhuma mensagem original.
O Spring pode encapsular uma exception do Hibernate, por exemplo, em uma outra
exception de sua árvore, isso é igual para as exceptions do JDO e da JPA também. Este
mecanismo do Spring lhe evita a irritação de ter que tratar exceptions que você não pode
se recuperar.
• Abre a conexão
• Especifica um Statement
• Processa as exceptions
• Gerencia as transações
• Fecha a conexão
Dessa forma o desenvolvedor pode trabalhar com uma camada de mais alto nível e mais
produtivas. Essa camada está dividida basicamente em quatro pacotes, core, datasource,
object e suppport que provem objetos de acesso a dados e call backs além de datasources
e facilidades para acessar stored procedures e functions de bancos.
No Spring é possível configurar sua fonte de dados de forma que seja possível obter
o DataSource de uma fonte JNDI. Podemos utilizar um DriverManagerDataSource que é
uma simples implementação de DataSource que devemos informar o driver a ser utilizado,
conforme exemplo abaixo:
</beans>
Para criarmos esse objeto é necessário informar o driver, a url de conexão para o
banco, usuário e a senha. Nesse caso está sendo utilizado uma conexão com o banco de
dados HSQLDB. Esse DataSource não disponibiliza pools de conexões, a cada chamada será
criada uma conexão nova.
Dica: O Spring framework faz a tradução de erros de SQL Genéricos para Exceptions mais
específicas, se você desenvolve alguma regra ou processamento em stored procedures ou
functions de bancos e lavanta exceptions personalizadas no banco, podemos traduzir essas
exceptions personalizadas para exceptions do Java.
Para fazer isso é necessário herdar a classe SQLErrorCodeSQLExceptionTranslator e
implementar o método custom translate onde você irá traduzir as exceptions.
<bean
id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate"
lazy-init="false"
>
<property name="dataSource" ref="dataSource" />
<property name="lazyInit" value="false" />
</bean>
</beans>
A Classe JdbcTemplate tem uma série de métodos utilitários, dentre eles podemos
destacar:
• Execute (String sql): É utilizado para executar comandos ddl.
• Update (String sql,Object[] args): É utilizado para executar inserts e updates esse
método recebe um Object[] por que ele cria um PreparedStatement. Esse método
pode ser utilizado para efetuar operações de delete também.
• queryForInt (String sql): Método que já retorna um int primitivo a partir de uma
query SQL, é muito útil quando temos que buscar apenas um valor no banco em
alguma tabela de configuração.
• queryForList (String sql): Método que executa uma query SQL e trás um java.util.List
como resultado da consulta.
A Classe JdbcTemplate possui vários overrides dos métodos de acesso a dados para as
mais diversas situaçãoes como SQL simples, PreparedStatements, RowMapper, SQL Types.
Veja um exemplo completo de acesso a dados com os principais métodos do
JdbcTemplate no código abaixo:
<bean
id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate"
lazy-init="false"
>
<property name="dataSource" ref="dataSource" />
<property name="lazyInit" value="false" />
</bean>
</beans>
package com.targettrust.spring.jdbc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
JdbcTemplate jt = (JdbcTemplate)ac.getBean("jdbcTemplate");
try{
jt.execute("create table _teste (nome varchar(50), numerox
integer); ");
}catch(UncategorizedSQLException e){
System.out.println("A Tabela já existe! ");
jt.update("delete from teste ");
}
System.out.println(
jt.queryForObject("select nome from teste where nome = 'p3'
", String.class)
);
});
System.out.println(ot);
}
}
O Spring prove suporte para gerenciar uma SessionFactory do Hibernate. Essa tarefa
pode ser feita através do LocalSessionFactoryBean. Esse bean necessita de uma DataSource
e de recursos de mapeamentos podendo ser um hibernate mappings ou configurações de
mapeamento feitas no próprio Spring que seram repassadas ao Hibernate. É muito comum
utilizar esse tipo de recurso em uma aplicação JEE ou desktop.
<bean
id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
lazy-init="false"
>
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>com/targettrust/spring/hibernate/Pessoa.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.PointbaseDialect
</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
</beans>
package com.targettrust.spring.hibernate;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
SessionFactory sf = (SessionFactory)ac.getBean("sessionFactory");
System.out.println(sf.openSession());
}
}
Código 4.2 TesteSessionFactory.java
Nesse exemplo foi necessário utilizar o hsqldb.jar, pois nesse jar que estão os drivers de
acesso ao banco de dados hsqldb, se você quiser utilizar outro banco de dados em sua
aplicação, você deverá trocar esse jar pelo jar apropriado para seu banco de dados.
Hibernate Template
package com.targettrust.spring.hibernate;
public Pessoa() {}
@Override
public String toString() {
return "nome: " + nome + ", email: " + email;
}
}
Código 4.3 Pessoa.java
<bean
id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
lazy-init="false"
>
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>com/targettrust/spring/hibernate/Pessoa.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop
key="hibernate.dialect">org.hibernate.dialect.PointbaseDialect</prop>
<!--<prop key="hibernate.hbm2ddl.auto">create</prop> --
>
</props>
</property>
</bean>
<bean
id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate"
lazy-init="false"
>
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
XML 4.6 Configuração dos beans do Spring.
package com.targettrust.spring.hibernate;
import java.util.List;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateTemplate;
HibernateTemplate ht =
(HibernateTemplate)ac.getBean("hibernateTemplate");
testeFind(ht);
testeDetachedCriteria(ht);
testeSave(ht);
testeDelete(ht);
testeGet(ht);
try{
ht.saveOrUpdate(p);
ht.flush();
}catch(DataAccessException e){
System.out.println(e.getMessage());
}
}
}
Código 4.4 HibernateTemplateTeste.java
Transações Declarativas
Para ter esse tipo de recurso é necessário ter um servidor de aplicação como, por
exemplo, JBOSS ou ORACLE IAS, mas se estou em uma aplicação java que roda fora de
uma servidor JEE? Estou fadado a trabalhar com o gerenciamento de transações em baixo
nível programaticamente? Se você usa Spring framework a resposta é não! O Spring é
capaz de fazer isso por nós e ainda mais, com Spring é possível ter transações declarativas
com aplicações JDBC.
De forma programática podemos ter controle transacional através do
TransactionTemplate do Spring que pode ser utilizado com o Hibernate.
HibernateTransactionManager é o TransactionManager do Spring para hibernate 3, esse
objeto necessita de uma SessionFactory. Com o transactionTemplate podemos executar
um código através do método execute que pode receber um
TransactionCallBackWithoutResult para realizarmos alguma operação no banco. Apesar de
o Spring facilitar esse trabalho, ainda assim essa abordagem é custosa e pouco produtiva.
A alternativa para esse problema pode ser o suporte de transações declarativas do
Spring framework. Esse mecanismo substitui as chamadas programáticas por interceptor
AOP aliados a proxys para adicionar o controle transacional.
Dessa forma o componente pode se focar nas regras de negócio e o controle
transacional ficará isolado de forma que poderemos trocar essa implementação sem afetar
as regras de negócio da aplicação.
Com o mecanismo de transações declarativas do Spring é possível utilizar um
contexto de transação gerenciado pelo HibernateTransactionManager com uma única
SessionFactory em uma ThreadLocal e depois trocar para transações distribuídas com JPA
apenas trocando o manager de transação, sem afetar nada no seu código já escrito.
Antes do Spring 2.0 nos Springs 1.X.X era utilizado o TransactionProxyFactoryBean,
ainda é possível utilizar esse recurso, o Spring prove total compatibilidade com isso.
Existe uma grande vantagem em utilizar o HibernateTransactionManager por que
ele irá expor a transação a nível de conexão JDBC por DataSource, ou seja, se criarmos um
JdbcTemplate com o mesmo DataSource da SessionFactory do Transactionanager teremos
controle transacional entre Hibernate e JDBC de forma declarativa.
Agora a configuração AOP para indicar como as classes devem ser interceptadas
pela transação declarativa e quando interceptadas que advice elas devem utilizar. Aqui
informamos que queremos interceptar todos os métodos da classe ProdutoService, não
importando o método e os parâmetros passados. Nessa configuração foi informado para
utilizar um Advice.
No Advice estamos definindo que métodos devem ter escopo transacional de fato.
Os métodos definidos são: findAll que necessitará de uma transação ativa, se essa
transação não estiver ativa ele irá criar uma nova, esse método está marcado como
somente leitura, ou seja, ele só faz consultas na base. O outro método é o salvar* , significa
que todos os métodos que comecem com salvar serão transacionados e terão
comportamento igual ao findAll, porém esses métodos salvar podem fazer modificações
na base de dados.
<!-- Definição do Advide AOP que casa o gerente de transações com os métodos
Esse beans informa que métodos e como esses métodos devem
ser tratados pela transação -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="salvar*" propagation="REQUIRED" /> <tx:method
name="findAll" propagation="REQUIRED" read-only="true"/>
</tx:attributes></tx:advice>
XML 4.7 TxAdvice
Quando o método salvar do ProdutoService for invocado, o Spring irá criar uma
nova transação se não existir nenhuma ativa, poderíamos trocar essa forma de isolamento
transacional. Poderíamos usar REQUIRED_NEW que força a criação de uma nova transação
a cada chamada ao método.
Exercícios
5. Facilitadores
Objetivos
Envio de E-mails
• JAF (activation.jar)
Agora vamos ver um exemplo prático de como enviar e-mails com Spring.
Primeiro vamos configurar um MailSender, nesse caso será o JavaMailSender, ao
definir esse bean precisamos setar as propriedades: host, password, username, onde
host é o servidor de e-mail e username e password são usuário e senha.
<bean
id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl"
>
<property name="host" value="host.url.com.br" />
<property name="password" value="senha" />
<property name="username" value="username@host.com.br" />
</bean>
XML 5.1 Definição do bean JavaMailSender
<bean
id="templateMessage"
class="org.springframework.mail.SimpleMailMessage"
>
<property name="from" value= "user@host.com.br" />
<property name="subject" value="Teste e-mails com Spring" />
</bean>
XML 5.2 Definição do bean templateMessage
<bean
id="simpleMailMessage"
class="org.springframework.mail.SimpleMailMessage"
>
<constructor-arg index="0" ref="templateMessage" />
<property name="to" value="dmetallica@gmail.com" />
<property name="text" value=" 123 Testando... " />
</bean>
Agora vem o programa Java, que faz o envio do e-mail de fato.É requisitado
no contexto do Spring um SimpleMailMessage que é a mensagem e um MailSender
para o envio.
package com.targettrust.spring.email;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
ApplicationContext ac =
new
ClassPathXmlApplicationContext("/com/targettrust/spring/email/Spring-
beans.xml");
SimpleMailMessage msg =
(SimpleMailMessage)ac.getBean("simpleMailMessage");
MailSender ms = (MailSender)ac.getBean("mailSender");
try{
ms.send(msg);
}
catch(MailException ex) {
ex.printStackTrace();
}
}
}
package com.targettrust.spring.email;
import javax.mail.Message;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessagePreparator;
JavaMailSender ms = (JavaMailSender)ac.getBean("mailSender");
package com.targettrust.spring.email;
import javax.mail.internet.MimeMessage;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
"/com/targettrust/spring/email/Spring-beans.xml");
JavaMailSender ms = (JavaMailSender)ac.getBean("mailSender");
try{
MimeMessage message = ms.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setFrom("form@server.com.br");
helper.setTo("dest@server.com");
helper.setText("Email MimeMessageHelper");
ms.send(message);
}catch(Exception e){
e.printStackTrace();
}
}
}
Código 5.3 TesteMimeMessageHelper.java
package com.targettrust.spring.email;
import java.io.File;
import javax.mail.internet.MimeMessage;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
"/com/targettrust/spring/email/Spring-beans.xml");
JavaMailSender ms = (JavaMailSender)ac.getBean("mailSender");
try{
MimeMessage message = ms.createMimeMessage();
ms.send(message);
}catch(Exception e){
e.printStackTrace();
}
}
}
Código 5.4 TesteMimeMessageHelper.java com envio de anexos.
package com.targettrust.spring.email;
import java.io.File;
import javax.mail.internet.MimeMessage;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
"/com/targettrust/spring/email/Spring-beans.xml");
JavaMailSender ms = (JavaMailSender)ac.getBean("mailSender");
try{
MimeMessage message = ms.createMimeMessage();
MimeMessageHelper helper = new
MimeMessageHelper(message,true);
helper.setFrom("form@server.com.br");
helper.setTo("dest@server.com");
helper.setText("<html><body><img
src='cid:img1'><br>Email MimeMessageHelper com suporte a imagems em
linha!</body></html>",true);
ms.send(message);
}catch(Exception e){
e.printStackTrace();
}
}
}
Código 5.5 TesteMimeMessageHelper.java com envio de anexos em linha html.
Importante: Para que a adição de recursos em linha funcione, você deve adicionar
primeiro o text e depois o recurso respeitando o cid. Se não, pode ser que não
funcione corretamente.
package com.targettrust.spring.jdktask;
import java.util.Date;
import java.util.TimerTask;
@Override
@SuppressWarnings("deprecation")
public void run() {
System.out.println(new Date().getHours() + ":" +
new Date().getMinutes() + ":" +
new Date().getSeconds()
);
}
}
Código 5.6 HoraCertaService.java
<bean
id="horaCertaService"
class="com.targettrust.spring.jdktask.HoraCertaService"
/>
<bean
id="scheduledTask"
class="org.springframework.scheduling.timer.ScheduledTimerTask"
lazy-init="false"
>
<!-- Espera 0 ms antes de iniciar -->
<property name="delay" value="0" />
<!-- roda de 1 em 1 segundo -->
<property name="period" value="1000" />
<!-- Ira executar a TimerTask horaCertaService -->
<property name="timerTask" ref="horaCertaService" />
</bean>
</beans>
XML 5.4 ScheduledTimerTask bean
Você pode ver que através da propriedade timerTask ligamos o nosso service
de HoraCerta com esse agendamento de tarefa.
Agora só falta injetarmos essa configuração em uma factory de
agendamento de tempo.
<bean
id="timerFactory"
class="org.springframework.scheduling.timer.TimerFactoryBean"
>
<property name="scheduledTimerTasks">
<list>
<ref bean="scheduledTask" />
</list>
</property>
</bean>
XML 5.5 TimerFactoryBean bean
<bean
id="horaCertaService"
class="com.targettrust.spring.jdktask.HoraCertaService"
/>
<bean
id="scheduledTask"
class="org.springframework.scheduling.timer.ScheduledTimerTask"
lazy-init="false"
>
<!-- Espera 0 ms antes de iniciar -->
<property name="delay" value="0" />
<!-- roda de 1 em 1 segundo -->
<property name="period" value="1000" />
<!-- Ira executar a TimerTask horaCertaService -->
<property name="timerTask" ref="horaCertaService" />
</bean>
<bean
id="timerFactory"
class="org.springframework.scheduling.timer.TimerFactoryBean"
>
<property name="scheduledTimerTasks">
<list>
<ref bean="scheduledTask" />
</list>
</property>
</bean>
</beans>
XML 5.6 Spring-beans.xml
package com.targettrust.spring.jdktask;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
package com.targettrust.spring.jdktask;
import java.util.Date;
@SuppressWarnings("deprecation")
public void showTime() {
System.out.println(new Date().getHours() + ":" +
new Date().getMinutes() + ":" +
new Date().getSeconds()
);
}
}
Código 5.8 HoraCertaServiceNaoAcoplada.java
Perceba que o nome da classe foi alterado para enfatizar que esse é um
outro artefato e seu código foi modificado de fato. Agora veja como ficam as
injeções do Spring:
<bean
id="horaCertaServiceNaoAcoplada"
class="com.targettrust.spring.jdktask.HoraCertaServiceNaoAcoplada"
/>
<bean
id="scheduledTask"
class="org.springframework.scheduling.timer.ScheduledTimerTask"
lazy-init="false"
>
<!-- Espera 0 ms antes de iniciar -->
<property name="delay" value="0" />
<!-- roda de 1 em 1 segundo -->
<property name="period" value="1000" />
<!-- Irá executar a TimerTask horaCertaService -->
<property name="timerTask" ref="executor" />
</bean>
<bean
id="executor"
class="org.springframework.scheduling.timer.MethodInvokingTimerTask
FactoryBean"
>
<property name="targetObject" ref="horaCertaServiceNaoAcoplada" />
<property name="targetMethod" value="showTime" />
</bean>
<bean
id="timerFactory"
class="org.springframework.scheduling.timer.TimerFactoryBean"
>
<property name="scheduledTimerTasks">
<list>
<ref bean="scheduledTask" />
</list>
</property>
</bean>
</beans>
XML 5.7 Spring-beans-2.xml
O Que foi feito? Foi definido um bean chamado executor e esse bean é um
MethodInvokingTimerTaskFactoryBean, onde a propriedade targetObject define
qual objeto será invocado e a propriedade targetMethod define qual método será
chamado.
Depois esse bean é associado ao scheduledTask pela propriedade
timerTask=”executor”.
package com.targettrust.spring.jdktask;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
@Aspect Support
Vamos criar uma interface Service, que define o que os Services devem fazer.
package com.targettrust.spring.aop;
package com.targettrust.spring.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
package com.targettrust.spring.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@Override
public void fazAlgo() {
log.info("Fiz algo do tipo B");
}
}
Código 5.12 ServiceB.java
package com.targettrust.spring.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@Override
public void fazAlgo() {
log.info("Fiz algo do tipo C");
}
}
Código 5.13 ServiceC.java
Agora vamos registrar os beans no Spring, esse registro é normal, bean id, classe
e o proxy aop.
<aop:aspectj-autoproxy/>
<bean
id="aspecto"
class="com.targettrust.spring.aop.Aspecto"
lazy-init="false"
/>
<bean
id="sa"
class="com.targettrust.spring.aop.ServiceA"
/>
<bean
id="sb"
class="com.targettrust.spring.aop.ServiceB"
/>
<bean
id="sc"
class="com.targettrust.spring.aop.ServiceC"
/>
<bean
id="services"
class="java.util.ArrayList"
>
<constructor-arg index="0">
<list>
<ref bean="sa" />
<ref bean="sb" />
<ref bean="sc" />
</list>
</constructor-arg>
</bean>
</beans>
XML 5.8 Spring-beans.xml
package com.targettrust.spring.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class Aspecto {
@Before("execution(* com.targettrust.spring.aop.Service.*(..))")
public void execucaoDeFazAlgoAntes() {
log.info("To sabendo antes da execução de Service");
}
@After("execution(* com.targettrust.spring.aop.Service.*(..))")
public void execucaoDeFazAlgoDepois() {
log.info("To sabendo depois da execução de Serice");
}
@Around("execution(*
com.targettrust.spring.aop.ServiceB.faz*(..)))")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws
Throwable {
Object retVal = pjp.proceed();
log.info("To sabendo around SericeB");
return retVal;
}
}
Código 5.14 Aspecto.java
package com.targettrust.spring.aop;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ApplicationContext ac = new
ClassPathXmlApplicationContext("/com/targettrust/spring/aop/Spring-
beans.xml");
List<Service> services =
(List<Service>)ac.getBean("services");
for(Service s: services){
s.fazAlgo();
}
}
}
Código 5.15 TesteAop.java
Testing Support
O Spring framework prove integração com frameworks de teste unitário
como por Junit e o TestNG. Dentre os recursos mais importantes podemos destacar
auto-wire automática para propriedades protected e rollback no final da execução.
Esse último recurso é importantíssimo, pois podemos rodar um test e no final ele
sempre dará rollback, assim não iremos sujar a base de dados.
O Spring framerwork permite o uso de frameworks de mock objects como,
por exemplo, o easy mock. Isso é muito útil, pois dessa forma podemos testar
apenas a camada de services sem persistir na camada DAO, sendo que a camada
DAO pode ser composta de mocks. Assim isolando o teste dos Services e deixando
o teste rápido e funcional.
É possível rodar testes integrados sem a necessidade de se fazer deploy no
servidor de aplicação. Assim podemos testar recursos de SQL, Hibernate e transação
sem o servidor de aplicação, isso dá melhor desempenho aos testes.
O mecanismo de test do Spring prove gerenciamento do contexto e cache,
para promover a performance.
AbstractDependencyInjectionSpringContextTests
Essa classe do Spring prove as facilidades de gerenciamento de contexto do
Spring, assim o primeiro método de test a ser executado será mais lento, porém os
outros vão ser muito mais rápidos pois utilizaram o contexto que já foi iniciado.
Para utilizar esse recurso é necessário implementar o método:
protected String[] getConfigLocations() que deve retornar um array de String
com os arquivos de configurações do Spring.
O Spring utiliza seus recursos de autoWire by type para dar produtividade
aos testes. Basta declarar um objeto e prover um setter que o Spring irá injetar
automaticamente esse objeto para você.
package com.targettrust.spring.testing;
import java.util.Date;
package com.targettrust.spring.testing;
import java.util.Date;
import junit.framework.Assert;
import
org.springframework.test.AbstractDependencyInjectionSpringContextTests;
@Override
protected String[] getConfigLocations() {
return new
String[]{"classpath:com/targettrust/spring/testing/Spring-beans.xml"};
}
@SuppressWarnings("deprecation")
public void testDataDoDataService(){
Date d = dataService.getSysDate();
Date l = new Date();
Assert.assertEquals(d.getDay(),l.getDay());
Assert.assertEquals(d.getMonth(),l.getMonth());
Assert.assertEquals(d.getYear(),l.getYear());
}
}
Código 5.18 TesteDataService.java classe de teste de fato.
package com.targettrust.spring.testing;
import java.util.Date;
import junit.framework.Assert;
import
org.springframework.test.AbstractDependencyInjectionSpringContextTests;
public TestDataServiceWithProtected() {
super.setPopulateProtectedVariables(true);
}
@Override
protected String[] getConfigLocations() {
return new
String[]{"classpath:com/targettrust/spring/testing/Spring-beans.xml"};
}
@SuppressWarnings("deprecation")
public void testDataDoDataService(){
Date d = dataService.getSysDate();
Date l = new Date();
Assert.assertEquals(d.getDay(),l.getDay());
Assert.assertEquals(d.getMonth(),l.getMonth());
Assert.assertEquals(d.getYear(),l.getYear());
}
}
Código 5.19 TesteDataServiceWithProtected.java classe de teste de fato.
AbstractAnnotationAwareTransactionalTests
AbstractTransactionalDataSourceSpringContextTests
Essa classe de testes é utilizada para adicionar o suporte a transações, e no
final do teste irá fazer roll back automaticamente. Se existir a necessidade de
efetuar o commit por causa de seu banco de dados, você pode executar o método
super.setComplete() no construtor, isso fará com que o commit seja executado ao
invés do rollback.
Veja como utilizar esse recurso na prática com esse teste abaixo:
package com.targettrust.spring.testing;
import org.springframework.jdbc.core.JdbcTemplate;
import
org.springframework.test.AbstractTransactionalDataSourceSpringContextTest
s;
import com.targettrust.spring.transaction.Produto;
import com.targettrust.spring.transaction.ProdutoService;
public TesteTransactionWithRollBack() {
//super.setComplete();
super.setDefaultRollback(true);
super.setPopulateProtectedVariables(true);
}
@Override
protected String[] getConfigLocations() {
return new
String[]{"classpath:/com/targettrust/spring/transaction/Spring-
beans.xml"};
}
produtoService.salvar(p);
System.out.println(produtoService.findAll());
}
package com.targettrust.spring.remoting;
import java.util.Date;
Agora vamos fazer uma implementação de serviço padrão para essa interface.
package com.targettrust.spring.remoting;
import java.util.Calendar;
import java.util.Date;
<bean
class="org.springframework.remoting.rmi.RmiServiceExporter"
>
<property name="serviceName" value="Target-HoraService"/>
<property name="service" ref="horaService"/>
<property name="serviceInterface"
value="com.targettrust.spring.remoting.HoraService"/>
<property name="registryPort" value="1199"/>
</bean>
</beans>
XML 5.10 server-beans.xml
package com.targettrust.spring.remoting;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
@SuppressWarnings("unused")
public static void main(String[] args) throws Throwable {
ApplicationContext ac =
new
ClassPathXmlApplicationContext("/com/targettrust/spring/remoting/server-
beans.xml");
while(true){
Thread.sleep(10000L);
}
}
}
Código 5.23 ContextServer.java
Agora vamos a configuração xml do cliente, que poderia ser um programa Java
com RMI normal, nesse exemplo vamos utilizar os próprios recursos do Spring para
isso.
</beans>
XML 5.11 cliente-beans.xml
package com.targettrust.spring.remoting;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
HoraService hs = (HoraService)ac.getBean("horaService");
System.out.println("Hora vinda do Service: " + hs.getDate());
}
}
Código 5.24 ContextCliente.java
Exercícios