Académique Documents
Professionnel Documents
Culture Documents
Spring Framework
Caelum www.caelum.com.br
Ensino e Inovao
ri
Caelum
Ensino e inovao
A Caelum atua no mercado com consultoria, desenvolvimento e ensino em computao. Sua equipe
participou do desenvolvimento de projetos em vrios clientes e, aps apresentar os cursos de vero de Java
na Universidade de So Paulo, passou a oferecer treinamentos para o mercado. Toda a equipe tem uma
forte presena na comunidade atravs de eventos, artigos em diversas revistas, participao em muitos
projetos open source como o VRaptor e o Stella e atuao nos fruns e listas de discusso como o GUJ.
Com uma equipe de mais de 80 profissionais altamente qualificados e de destaque do mercado, oferece
treinamentos em Java, Ruby on Rails e Scrum em suas trs unidades - So Paulo, Rio de Janeiro e
Braslia. Mais de 8 mil alunos j buscaram qualificao nos treinamentos da Caelum tanto em nas unidades
como nas prprias empresas com os cursos incompany.
O compromisso da Caelum oferecer um treinamento de qualidade, com material constantemente
atualizado, uma metodologia de ensino cuidadosamente desenvolvida e instrutores capacitados
tecnicamente e didaticamente. E oferecer ainda servios de consultoria gil, mentoring e desenvolvimento
de projetos sob medida para empresas.
Comunidade
Nossa equipe escreve constantemente artigos no Blog da Caelum que j conta
com 150 artigos sobre vrios assuntos de Java, Rails e computao em geral.
Visite-nos e assine nosso RSS:
blog.caelum.com.br
O GUJ maior frum de Java em lngua portuguesa, com 700 mil posts e 70 mil
usurios. As pessoas da Caelum participam ativamente, participe tambm:
.~~ www.guj.com.br
Assine nossa Newsletter para receber notcias tcnicas de Java, Rails, Agile e
Web, alm de novidades, eventos e artigos:
www.caelum.com.br/newsletter
ndice
1.4 Bibliografia
Spring Framework
3.1 O que ?
10
12
12
13
13
14
17
18
21
4 Spring MVC
21
21
22
22
23
4.6 Configurando
23
24
24
25
26
26
27
28
28
29
30
31
32
4.19 Escopos
32
33
35
35
35
5.3 ProdutoHibernateDAO
36
5.4 @Repository
37
5.5 Exerccios
37
5.6 @Qualifier
39
5.7 Exerccios
39
39
5.9 Exerccios
40
40
41
ii
41
5.13 Exerccios
42
43
44
44
5.17 Entitidade
44
5.18 Exerccios
46
49
5.20 Exerccios
49
50
51
5.23 Exerccios
51
52
53
55
6.1 Introduo
55
55
55
6.4 Exerccios
56
57
6.6 Exerccios
58
59
6.8 Exerccio
60
7 Spring Security
61
61
62
62
63
64
67
7.7 Login
70
71
iii
75
75
76
77
77
77
78
79
8.8 Exerccios
81
82
83
84
85
85
86
89
89
90
iv
CAPTULO
A importncia da injeo de dependncias hoje to grande que, em uma recente entrevista com os autores
do livro "Design Patterns" em comemorao aos 15 anos do livro, Erich Gamma, um dos autores, disse que, se
houvesse uma segunda edio do livro, um padro a ser includo seria Dependency Injection:
http://bit .1y/entrevista-design-patterns
Spring Framework 3
Caelum http://www.caelum.com.br
Fora isso, sinta-se vontade para entrar em contato com seu instrutor e tirar todas as dvidas que tiver
durante o curso.
1.4 - Bibliografia
possvel aprender muitos dos detalhes e pontos no cobertos no curso em tutoriais na Internet em portais
como o GUJ, em blogs (como o da Caelum: http:,' blog.caelum.com.br) e em muitos Sites especializados.
Mas se voc deseja algum livro para expandir seus conhecimentos ou ter como guia de referncia, temos
algumas indicaes dentre vrias possveis:
Sobre Java e melhores prticas
CAPTULO
Voltaire
Nesse captulo, voc vai entender:
o que Injeo de Dependncias e que problemas ela resolve;
o que Inverso de Controle e tipos de inverses;
como usar frameworks para satisfazer as dependncias do seu projeto.
Para persistirmos os produtos, usaremos o padro DAO. Nosso DAO ser modelado como uma interface
Java. Desta forma, poderemos ter vrias implementaes do DAO e troc-las conforme necessrio.
As operaes que teremos no ProdutoDAO:
package br.com.caelum.estoque.dao;
public interface ProdutoDAO {
void salvar(Produto produto);
void alterar(Produto produto);
List<Produto> listar();
Produto buscarPorld(Long id);
}
Caelum http://www.caelum.com.br
Spring Framework 3
No curso, ser particularmente interessante ter o DAO como interface, j que vamos ver Hibernate apenas
mais pra frente. At l, para podermos criar nosso sistema usando persistncia, vamos criar uma implementao simples do DAO em memria. Mais pra frente, vamos troc-la por uma implementao completa com
Hibernate.
Nosso ProdutoMemoriaDAO, por enquanto apenas com os mtodos de listagem e adio implementados:
package br.com.caelum.estoque.dao;
public class ProdutoMemoriaDA0 implements ProdutoDA0 {
private List<Produto> produtos = new ArrayList<Produto>();
public ProdutoMemoriaDA0() {
Produto produto = new Produto();
produto.setDescricao("Mac Book");
produto.setQuantidade(40);
produtos.add(produto);
}
Teremos tambm uma classe GerenciadorDeProduto responsvel por pedir dados de um Produto ao usurio,
depois persistir esse objeto e por fim mostrar alguma confirmao.
Se estivssemos na Web, seria equivalente a pegar os dados do request enviado por um formulrio, persistir
e depois mostrar uma pgina de confirmao. Vamos ver a parte Web em um captulo mais a frente. Por
enquanto, nossa classe GerenciadorDeProduto ser:
Captulo 2 - Injeo de dependncias e Inverso de Controle - Dependncias e o alto acoplamento - Pgina 4
Caelum http://www.caelum.com.br
Spring Framework 3
uma classe bastante simples. Ela apenas controla a execuo das coisas entre o modelo, o DAO e a
interao com o usurio.
Mas ela tem um problema. Nossa classe GerenciadorDeProduto est acoplada no s interface
ProdutoDAO como implementao concreta ProdutoMemoriaDAO. Repare que o construtor conhece a implementao especfica e d new na classe.
Dizemos que a classe tem uma dependncia tanto com ProdutoDAO quanto com ProdutoMemoriaDAO.
Mas, quando olhamos a lgica de negcios propriamente dita, no mtodo novoProduto, percebemos que
o que realmente necessrio para execut-la algum DAO. A lgica em si no depende de qual instncia
especificamente da implementao que estamos usando. (imagine, como ser nosso caso daqui a pouco, que
tenhamos mais de uma implementao, como a ProdutoHibernateDAO)
No fundo, precisamos ento apenas de algum ProdutoDAO mas acabamos tendo que dar new diretamente
na implementao para us-la. Nossa aplicao resolve a implementao e a dependncia, chamando
diretamente a classe especfica.
O problema desse tipo de chamada que, no dia que precisarmos mudar a implementao do DAO, precisaremos mudar nossa classe. Agora imagine que tenhamos 10 classes no sistema que usem nosso ProdutoDAO.
Se todas do new na implementao especfica a ser usada (agora, ProdutoMemoriaDAO), quando formos mudar
a implementao para, digamos, ProdutoHibernateDAO, precisaremos mudar em vrios lugares.
E ainda ainda um segundo problema em gerenciar a dependncia diretamente na aplicao. Se a outra
classe precisar de um processo de construo mais complicado, precisaremos nos preocupar com isso. Imagine
a futura classe ProdutoHibernateDAD receba uma session do Hibernate. Ou ento uma ProdutoJDBCDAD que
precise da conneotion. Se formos dar new nessas classes, precisaramos resolver essas exigncias que, se
formos pensar bem, nem deveriam ser responsabilidades nossas.
Caelum http://www.caelum.com.br
Spring Framework 3
Repare que, agora, nossa classe no est mais acoplada implementao alguma do ProdutoDAO nem
import do ProdutoMemoriaDA0 existe mais. O que nossa classe diz que ela depende de algum ProdutoDAO
-
sem acoplar-se a nenhuma implementao especfica e sem se preocupar em como resolver essa dependncia.
Mas quem for usar essa minha classe, agora, vai precisar satisfazer as dependncias. Pode parecer que
estamos apenas empurrando o problema, mas temos que olhar as classes isoladamente. Perceba que, do
ponto de vista apenas da GerenciadorDeProduto, estamos recebendo nossas dependncias no construtor. Elas
esto sendo injetadas para ns. Como isso vai acontecer de verdade no problema nosso.
E, o melhor, imagine que o DAO em uso aquele do Hibernate. E que a classe ProdutoHibernateDA0
tambm vai usar injeo de dependncias para recebera Session que ela precisa. Veja um esboo dessa
classe:
public class ProdutoHibernateDAO implements ProdutoDAO {
private Session session;
public ProdutoHibernateDAO(Session session) {
this.session = session;
}
Aqui, temos dois pontos importantes. O primeiro que a classe ProdutoHibernateDA0 est usando Dl para
no precisar se preocupar em abrir uma session (um processo complicado). Mas, mais importante, lembrar da
nossa GerenciadorDeProduto: imagine ter que gerenciar nossa dependncia do DAO diretamente e ter que se
preocupar no s com o ProdutoHibernateDA0 como com a dependncia de session que ele tem. Precisaramos
resolver as duas coisas, dando ainda mais trabalho.
Contudo, como usamos Dl na classe GerenciadorDeProjeto, nem sabemos qual a implementao do DAO
em uso e, muito menos, suas eventuais dependncias. Se gerenciar as prprias dependncias j ruim, o
que dizer de gerenciar as dependncias das dependncias.
Mas voltamos ao ponto de que, quem for usar a GerenciadorDeProjeto ter um imenso trabalho! Ele ter
que conhecer a GerenciadorDeProjeto, resolvera dependncia dela em ProdutoDAO, 0 que, por sua vez, far
ele conhecera implementao ProdutoHibernateDA0 e ainda suas dependncias em Session, alm de saber
como se cria uma Session. Parece muita coisa para uma classe s.
a que entram em cena os Frameworks de Injeo de dependncia. Em projetos maiores, teremos
muitas classes com muitas dependncias umas nas outras, tanto direta quanto indiretamente. Resolver esse
complexo grafo de dependncias na mo possvel mas muito trabalhoso. Existem frameworks cujo objetivo
resolver as dependncias.
Captulo 2 - Injeo de dependncias e Inverso de Controle - Injeo de dependncias - Pgina 6
Caelum http://www.caelum.com.br
Spring Framework 3
Registramos nesses frameworks todas as nossas classes e deixamos a cargo dele resolver as interdependncias entra elas. Nos preocupamos apenas em criar as classes usando o pattern de injeo de dependncias
e em registrar as classes nos frameworks.
E um dos frameworks mais importantes nessa parte de DI justo o Spring.
H ainda outros exemplos onde invertemos o controle de invocao do nosso cdigo e deixamos a cargo de
algum as chamadas no momento certo.
Spring Framework 3
Caelum http://www.caelum.com.br
No exemplo, poderamos fazer a classe GerenciadorDeProduto obter uma referncia de ProdutoDAO por meio
do Registry JNDI. Primeiro, precisamos registrara instncia do DAO que queremos (processo que chamado de
bind). Geralmente isto feito pelo container J2EE utilizando arquivos de configurao (deployment descriptors)
em um cdigo parecido com este:
//Registra do DO no Registry
Context ctx = new InitialContext();
ctx.bind("produtoDao", new ProdutoMemoriaDA0());
Nossa classe GerenciadorDeProduto pode, ento, obter uma referncia do tipo ProdutoDAO buscando no
Registry JNDI pelo nome registrado. Este processo tem o nome de lookup:
package br.com.caelum.fj27;
public class GerenciadorDeProduto {
private ProdutoDAO produtoDao;
public GerenciadorDeProduto() {
try{
Context ctx = new InitialContext();
produtoDao = (ProdutoDAO) ctx.lookup("produtoDao");
} catch (NamingException ex) {
// tratamento de exceo
}
// outros metodos
}
CAPTULO
Spring Framework
"Homens sbios fazem provrbios, tolos os repetem"
Samuel Palmer
Neste captulo, vamos ver o que o Spring Framework, qual sua arquitetura e qual seu princpio bsico de
funcionamento.
3.1 - O que ?
O Spring na verdade um framework de inverso de controle e no apenas de um framework de injeo
de dependncias. Ele faz mais coisas que apenas resolver suas dependncias, um framework completo de
servios para seus componentes.
O Spring Framework um container leve (lightweight container), ou container de POJO's segundo o pessoal do prprio Spring, com recursos de inverso de controle e orientao aspectos. O termo container
usado pelo Spring, pois ele gerencia o ciclo de vida dos objetos configurados nele, assim como um container
Java EE faz com os EJB's. Porm, considerado um container leve, pois ao contrrio do container Java EE em
suas primeiras verses, ele no intrusivo e as classes da aplicao no possuem dependncias com o Spring.
Alm disso, podemos dizer que o Spring tambm um framework de infraestrutura, pois prov servios
como gerenciamento de transaes e de segurana, alm de se integrar com frameworks ORM. Com ele voc
pode ter praticamente todos os benefcios de Java Enterprise Edition (JTA, JPA, JCA, Remoting, Servlets, JMS,
JavaMail, JDBC, etc) mas sem utilizar EJB's.
Container
Podemos dizer que containers so programas utilizados para gerenciar componentes de uma aplicao. Um exemplo de um container um container de servlets, onde sua funo gerenciar os
componentes de interface web que conhecemos como Servlets e JSPs.
Princpios do Spring
Spring Framework 3
Caelum http://www.caelum.com.br
Benefcios do Spring
ORM
DAO
Web
~orneie
JPA
TopLirik
Spring JDBC
Transaction
rnanngerrent
JEE
JDo
OJB
iBatie
JMX
JMS
JCA
Remoto-1g
EJBs
Ernail
AOP
Sprr
,
ksgectJ
AOP
integr3tion
Core
The InC container
Mdulo Core
O mdulo principal o Core e representa as principais funcionalidades do Spring, como o container para
inverso de controle e injeco de dependncias. Neste mdulo, o principal elemento a interface BeanFactory,
que prov uma implementaco do factory pattern, removendo do sistema a necessidade de singletons e desacoplando as dependncias de configuraces e especificaces da lgica de negcio do sistema.
Os outros so mdulos de integrao com diversas outras tecnologias que o Spring suporta, tais como:
Mdulo AOP
Spring AOP
Integrao com AspectJ
Caelum http://www.caelum.com.br
Spring Framework 3
Mdulo DAO
Spring JDBC
Gerenciamento de transaces
Mdulo ORM
Hibernate
JPA
TopLink
JDO
OJB
iBatis
Mdulo Java EE
JMX
JMS
JCA
Remoting
EJBs
Email
Mdulo Web
Spring Framework 3
Caelum http://www.caelum.com.br
e ele ainda usado em muitos projetos. Por isso, vamos primeiro ver como fazer as configuraes dos nossos
beans via XML antes de ver a abordagem com anotaes.
Veja a configurao de um bean no XML do Spring:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="produtoDao" class="br.com.caelum.estoque.dao.ProdutoMemoriaDAO" />
</beans>
Tag beans
A tag <beans> o elemento root do nosso arquivo XML. Dentro dela teremos as tags <bean> que representam
a configurao dos objetos do nosso sistema na forma de bean para o Spring.
Na tag
<bean>,
FQN
FQN significa FuIIy-Qualified Name, ou nome completo de uma classe. O FQN de uma classe
o nome da classe com o seu pacote completo. Por exemplo, o FQN da classe object
j ava . lang . Obj ect , o FQN da interface List j ava util Li st.
Caelum - http://www.caelum.com.br
Spring Framework 3
Criado o contexto do Spring, podemos pedir uma instncia de qualquer bean registrado l. Invocando o
framework diretamente, basta passar o id do componente:
ProdutoDA0 dao = (ProdutoDA0) context.getBean("produtoDao");
A partir da verso 3 do Spring podemos utilizar um segundo parmetro do mtodo getBean para definir o
tipo do bean. Assim nem preciso fazer o cast:
ProdutoDA0 dao = context.getBean("produtoDao", ProdutoDao.class);
No exemplo acima deve existir apenas um bean compatvel com a interface ProdutoDao. Caso contrrio o
Spring cria um exceo.
Spring Framework 3
Caelum http://www.caelum.com.br
package br.com.caelum.estoque;
public class Produto {
private Long id;
private String descricao;
private Integer quantidade;
//getters e setters
}
Caelum http://www.caelum.com.br
Spring Framework 3
package br.com.caelum.estoque.dao;
public interface ProdutoDA0 {
void salvar(Produto produto);
void alterar(Produto produto);
List<Produto> listar();
Produto buscarPorld(Long id);
}
4) Como ainda no vimos a integrao do Spring com Hibernate e nosso objetivo focar no Spring por enquanto, vamos criar uma implementao de DAO bem simples que apenas adiciona os produtos em uma
lista em memria.
Crie a classe ProdutoMemoriaDAO no pacote br. com. caelum.estoque.dao com a implementao simples do
DAO em memria. Ainda no implementaremos o mtodo de alterao:
package br.com.caelum.estoque.dao;
public class ProdutoMemoriaDA0 implements ProdutoDA0 {
private List<Produto> produtos = new ArrayList<Produto>();
5) Vamos criar agora nossa classe GerenciadorDeProduto. Mais tarde, vamos transform-la em um componente
de um sistema Web usando Spring MVC. Por isso, vamos faz-la um pouco diferente da que vimos antes.
Ao invs de mostrar uma mensagem de confirmao para o usurio, vamos apenas retornar uma String de
confirmao para ele. E o objeto Produto que pediramos para o usurio preencher com os dados, vamos
receber como argumento no mtodo.
Crie a classe
GerenciadorDeProduto
Spring Framework 3
Caelum http://www.caelum.com.br
package br.com.caelum.estoque;
public class GerenciadorDeProduto {
private ProdutoDA0 produtoDao;
public GerenciadorDeProduto(ProdutoDAO produtoDao) {
this.produtoDao = produtoDao;
}
Repare que, como estamos usando DI, declaramos a dependncia com o DAO no construtor.
6) Falta agora criar toda a configurao do Spring com os beans que acabamos de fazer.
a) Abra o arquivo chamado applicationContext.xml que se encontra no diretrio src do seu projeto. Este
o arquivo de configurao do Spring.
7) Para testarmos a injeo dos componentes que temos at agora, vamos utilizar uma classe simples com
mtodo main chamada Testelnjecao, que j existe em seu projeto.
Nela, criamos o ApplicationContext do Spring e, logo em seguida, pediremos uma instncia do nosso
gerenciador de produtos. Queremos obter essa instncia sem precisar satisfazer as dependncias dela.
Abra a classe
Testalnjecao
Caelum http://www.caelum.com.br
Spring Framework 3
package br.com.caelum.estoque;
public class Testalnjecao {
public static void main (String[] args) {
// cria o contexto do Spring
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
// obtm o gerenciador
GerenciadorDeProduto gerenciador =
context.getBean("gerenciadorProduto", GerenciadorDeProduto.class);
// cria um Produto como se o usurio tivesse preenchido um formulrio
Produto produto = new Produto();
produto.setDescricao("Livro Spring in Action");
produto.setQuantidade(10);
// chama a lgica e v o resultado
gerenciador.novoProduto(produto);
// verifica que o produto foi adicionao ao nosso dao em memoria
for (Produto p : gerenciador.getProdutos()) {
System.out.println("Descricao: " + p.getDescricao());
System.out.println("Quantidade: " + p.getQuantidade());
}
GerenciadorDeProduto
ProdutoDAO:
Repare que ainda estamos usando DI, mas declaramos agora a dependncia no setter.
2) Falta alterar a configurao do Spring para definir a injeo pelo setter.
Captulo 3 - Spring Framework - Exerccios opcionais: Injeco com Setter e Tipos Bsicos - Pgina 17
Spring Framework 3
Caelum http://www.caelum.com.br
3) Rode o Testalnjecao.
package br.com.caelum.estoque;
public class SpringBean {
}
private
private
private
private
List
String nome;
Integer quantidade;
Properties propriedades;
List<String> nomes;
Caelum http://www.caelum.com.br
Spring Framework 3
<value>Jose</value>
<value>Joao</value>
</list>
</property>
</bean>
Crie a classe
Testelnjecao2
no pacote
3
4
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
SpringBean springBean =
(SpringBean) applicationContext.getBean("springBean");
5
6
7
8
9
if (springBean != null) {
19
11
System.out.println("instanciado!");
System.out.println(springBean.getNome());
System.out.println(springBean.getQuantidade());
System.out.println(springBean.getPropriedades().get("cidade"));
12
13
14
15
16
17
18
19
20
21
22
Rode a classe e verifique na sada do console que todas os atributos foram injetados pelo Spring.
Caelum http://www.caelum.com.br
Spring Framework 3
CAPTULO
Spring MVC
"O verdadeiro discpulo aquele que supera o mestre."
Aristteles
Vamos usar como container Web o Apache Tomcat 6, que pode ser baixado em http: torncat.apache.org
21
Spring Framework 3
Caelum http://www.caelum.com.br
Importando o projeto
Caelum http://www.caelum.com.br
Spring Framework 3
Nesta configurao, se o Controller devolvesse, por exemplo, a string "index", o ViewResolver procuraria a
seguinte view para renderizar "WEB-INF/views/index.jsp".
Para executar o exemplo acima, basta chamar a URL da aplicao terminando em /ola.
Ex:
http:
O mtodo anotado com @RequestMapping, no tem restrio quanto a sua assinatura. A opo de retornar
uma String utilizada para informar qual view deve ser renderizada aps a execuo do mtodo.
No exemplo anterior, a view renderizada se chama ok e ter seu diretrio e extenso informadas pelo
ViewResolver. Por exemplo: WEB-INF/views/ok.jsp.
4.6 - Configurando
A anotao @Controller no lida por default. O Spring sempre usa o XML como padro e para usarmos
classes anotadas, temos que configurar o XML do Spring com a seguinte instruo:
<mvc:annotation driven/>
-
Spring Framework 3
Caelum http://www.caelum.com.br
xmlns:context="http://www.springframework.org/schema/context "
xmlns:mvc="http://www.springframework.org/schema/mvc "
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd ">
<beans>
package br.com.caelum.estoque.controller;
//importa
@Controller
public class ProdutosController {
@RequestMapping("index")
public String index() {
return "produtos/index";
}
2) Configure o XML do Spring para habilitar o suporte a anotao @Controller e informar onde o spring
deve procurar por beans anotados. Adicione as seguintes linhas ao seu app-config.xml no diretrio WEBINF/spring/app-config.xml:
<context:component-scan base-package="br.com.caelum.estoque" />
<mvc:annotation-driven />
3) Falta criar nossa View. Crie uma pasta chamada produtos em WEB-INF/views/ e, dentro, uma JSP chamada index.jsp com o seguinte contedo de teste:
<html>
Spring MVC!
</html>
4) Inicie o servidor e acesse seu controller chamando a URL http://localhost:8080 /estoque /index.html
Caelum http://www.caelum.com.br
Spring Framework 3
@Controller
public class ProdutosController {
@RequestMapping("listarProdutos")
public ModelAndView listar(){
List<Produto> produtos = new ArrayList<Produto>();
Produto macBook = new Produto();
macBook.setDescricao("Mac Book");
macBook.setQuantidade(100);
Produto ipad = new Produto();
ipad.setDescricao("IPad");
ipad.setQuantidade(200);
produtos.add(macBook);
produtos.add(ipad)
ModelAndView modelAndView = new ModelAndView("produto/lista");
modelAndView.addObject(produtos);
return modelAndView;
}
Nossa lista de produtos ento adicionada no objeto ModelAndView e o mtodo termina retornando o objeto
modelAndView.
Agora, podemos acessar a lista de produtos na JSP usando Expression Language. importante sabe qual
o nome que usamos para o acesso de nossa lista de produtos. O Spring utiliza uma conveno para isso. No
nosso exemplo, acessaramos da seguinte maneira na JSP:
<c:forEach items="${produtoList}" var="produto">
${produto.descricao} ${produto.quantidade} <br>
</c:forEach>
O nome produtoList gerado pelo Spring devido ao nosso List ser declarado com um tipo genrico Produto:
List<Produto>.
${stringList}
Spring Framework 3
Caelum http://www.caelum.com.br
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
@RequestMapping(value="/listar")
public ModelAndView listar(){
//.
.
@RequestMapping(value="/salvar")
public String salvar(){
}
Para acessar o mtodo listar () de ProdutosController precisamos chamar a seguinte a url que nosso
controller foi mapeado /produtos e ento a url do mtodo /listar. Ex: http:f locadhost:8080/estoque/produtos
listar.html
A anotao @Autowired deve ser utilizada para indicar uma dependncia e que a mesma deve ter seu valor
injetado.
@Controller
public class ProdutosController {
@Autowired
private ProdutoDA0 produtoDAO;
}
Caelum http://www.caelum.com.br
Spring Framework 3
S podemos injetar beans gerenciados em outros beans tambm gerenciados. No exemplo acima injetamos
O ProdutoDAO no ProdutosController porque ambos so gerenciados pelo o Spring as anotaes @component e
@Controller tem como objetivo o mesmo, sendo informar o Spring que ele deve gerenciar aqueles objetos.
@Component
public class ProdutoMemoriaDAO implements ProdutoDAO {
}
2) Vamos criar o mtodo que vai listar os produtos em nossa classe ProdutosController. O mtodo listar()
vai fazer uma chamada ao ProdutoDAO que vai ser injetado pelo Spring e adicionar seu resultado no objeto
ModelAndVlew.
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
@Autowired
private ProdutoDAO produtoDAO;
// metodo ndex()
@RequestMapping(value="/listar")
public ModelAndView listar() {
ModelAndView modelAndView = new ModelAndView("produtos/lista");
modelAndView.addObject(produtoDAO.listar());
return modelAndView;
}
3) Crie um JSP chamado Iista.jsp dentro do diretrio "WEB-INF/views/produtos/lista.jsp" para mostrar os produtos que so disponibilizados pela chamada do mtodo listar() de ProdutosController.
<Utaglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Lista Produtos</title>
</head>
<body>
<c:forEach items="UprodutoList}" var="produto"
${produto.descricao} - ${produto.quantidade}<br/>
</c : forEach>
</body>
</html>
Spring Framework 3
Caelum http://www.caelum.com.br
4.1:3 URLsanugavels
pode ser usada tambm para criao de urls bonitinhas. Supondo que temos o
e
ele tem um mtodo que deve listar um produto especifico de determinado id. O mProdutosController
todo poderia ter a seguinte estrutura:
@RequestMapping
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
@RequestMapping(value="/listar")
public ModelAndView listar(Long id){
//
}
E poderamos executar o mtodo atravs da url "/produtos/listar?idade=22" o parmetro idade seria passado
ao listar () que por sua vez filtraria o produto por seu id e ento devolveria para a View.
Ultimamente um recurso muito utilizado na web so as urls amigveis, que so urls mais legveis ao usurios.
Poderamos implementar nosso mtodo dessa forma:
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
@RequestMapping(value="/listar/{id}")
public ModelAndView listar(@PathVariable Long id){
//..
}
Agora podemos executar nosso mtodo atravs da seguinte url "produtos/listar/22" e assim o mtodo
listar () seria executado, o Spring por sua vez iria verificar que informamos que um valor vindo na url seria uma varivel {id} e que informamos que o mesmo iria ser passado ao mtodo @PathVariable
Caelumhttp://www.caelum.com.br
Spring Framework 3
Se tentarmos acessar a url "/produtos/listar/22" utilizando um formulrio com o verbo POST o spring no
permitira devido a uri ser ligada a utilizao do verbo GET.
package br.com.caelum.estoque.dao;
// imports
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
@Autowired
private ProdutoDA0 produtoDAO;
//metodo index()
@RequestMapping(value="/listar", method=RequestMethod.GET)
public ModelAndView listar() {
ModelAndView modelAndView = new ModelAndView("produtos/lista");
modelAndView.addObject(produtoDAO.listar());
return modelAndView;
}
@RequestMapping(value="/mostrar/{id}" , method=RequestMethod.GET)
public ModelAndView mostrar(@PathVariable Long id){
ModelAndView modelAndView = new ModelAndView("produtos/mostrar");
modelAndView.addObject(produtoDAO.buscarPorId(id));
return modelAndView;
}
Produto
<html>
<head>
<title>Detalhes do Produto</title>
</head>
<body>
Id : <input value="${produto.id}" disabled="disabled"/><br>
Descricao : <input value="${produto.descricao}" disabled="disabled"/><br>
Quantidade : <input value="${produto.quantidade}" disabled="disabled"/><br>
</body>
</html>
3) A listagem de produtos deve ser alterada para ter um link que vai chamar o mtodo mostrar, para o Produto
ser detalhado.
Captulo 4 - Spring MVC - Exercicios: Melhor uso do http - Pgina 29
Spring Framework 3
Caelum http://www.caelum.com.br
O Controller que tem que receber o objeto do tipo Produto como parmetro para o mtodo, deve declarar no
parmetro do mtodo que vai ser chamado.
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController{
@RequestMapping(value="/salvar")
public String salvar(Produto produto){
System.out.println(produto.getDescricao());
System.out.println(produto.getQuantidade());
return "ok";
}
O formulrio HTML, que vai fazer a requisio ao mtodo salvar () deve passar os parmetros que devem
ser setados no parmetro do mtodo. O nome dos parmetros sendo enviado devem seguir o nome do conjunto
de getters e setters do parmetro do mtodo sendo chamado. No nosso exemplo, o parmetro seria o Produto
e os getters e setters seriam para as propriedades descricao e quantidade.
<html>
<form action="/estoque/produtos/salvar.html">
<input type="text" name="descricao"/>
<input type="text" name="quantidade"/>
Caelum http://www.caelum.com.br
Spring Framework 3
tro que do tipo Produto. Ento o Spring MVC procura na requisio parmetros que tenham o nome de
alguma das propriedades do bean.
No exemplo acima, so enviados os parmetros descricao e quantidade. O Spring ento verifica que nosso
bean Produto tem duas propriedades com o mesmo nome. Lembrando que propriedades em Java o conjunto
get e set para um atributo, no exemplo getDescricao(), setDescricao() e getQuantidade(), setQua_ntidade().
Por exemplo:
@Controlller
@RequestMapping(value="/produtos")
public class ProdutosController {
@RequestMapping(value="/index")
public String index(){
return "produtos/listar.html"
}
@RequestMapping(value="/listar")
public ModelAndView listar(){
//....
}
No exemplo acima ao chamar /index o Spring faz o foward para /produtos/listar executando ento o mtodo
listar O. O forward o comportamento padro do spring mas podemos tambm fazer o redirect da ao.
@Controlller
@RequestMapping(value="/produtos")
public class ProdutosController {
@RequestMapping(value="/index")
public String index(){
return "redirect:produtos/listar.html"
}
@RequestMapping(value="/listar")
public ModelAndView listar(){
//
}
Spring Framework 3
Caelum http://www.caelum.com.br
2) Em ProdutosController vamos criar o mtodo farm(), que quando chamado direciona o usurio para a
pgina de cadastro. E o mtodo salvar () que recebe o Produto que est sendo cadastrado.
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
@Autowired
private ProdutoDAO produtoDAO;
// index()
//listar()
//mostrar()
@RequestMapping(value="/form" , method=RequestMethod.GET)
public String form() {
return "produtos/form";
}
@RequestMapping(value="/salvar" , method=RequestMethod.POST)
public String salvar(Produto produto) {
produtoDAO.salvar(produto);
return "redirect:/produtos/listar.html";
}
3) Inicie o servidor e teste o seu cadastro http: //localhost:8080/ estoque, produtos form.html
4.19 - Escopos
Perceba que ao injetar ProdutoMemoriaDA0 na classe ProdutosController sempre temos o mesmo objeto em
memria. S perdemos os dados cadastrados quando reiniciamos nosso servidor de aplicao. Isso acontece
devido aos escopos do Spring.
Caelum http://www.caelum.com.br
Spring Framework 3
Escopo Singleton
O escopo do tipo singleton define que ser criado uma nica instncia do bean para todas requisies
recebidas pelo Spring para aquele bean. O Spring garante que esta instncia ser nica para cada instncia
do loC container do Spring. Se tivermos um nico BeanFactory ou ApplicationContext, teremos uma nica
instncia de um bean do tipo singleton. singleton o padro.
Escopo Prototype
O escopo do tipo prototype define que ser criada uma nova instncia do bean para cada requisio recebida
pelo Spring para aquele bean.
Escopo Request
O escopo do tipo request define que o ciclo de vida do bean ser atrelado a um Request HTTP. O Spring
garante que cada request possui sua prpria instncia do bean. Ao final do request, o objeto descartado.
Escopo Session
O escopo do tipo session gerencia o ciclo de vida do bean baseado na HttpSession do usurio.
Captulo 4 - Spring MVC - Web Escopos: Request, Session, Global Session - Pgina 33
Caelum http://www.caelum.com.br
Spring Framework 3
Captulo 4 - Spring MVC - Web Escopos: Request, Session, Global Session - Pgina 34
CAPTULO
AnnotationSessionFactoryBean
A Factory Bean que o Spring possui a org. springframework.orm.hibernate3.annotation.AnnotationSessionFac
Esta classe deve ser configurada como um bean do Spring em seu xml de configurao.
A classe AnnotationSessionFactoryBean nos permite configurar todas as propriedades do Hibernate diretamente no arquivo do Spring, no necessitando o uso do arquivo de configurao do prprio do Hibernate
hibernate.cfg.xml.
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
destroy-method="destroy">
<property name="hibernateProperties">
<props>
<prop key="hibernate.connection.url">jdbc:mysql://localhost/fj27</prop>
<prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
<prop key="hibernate.connection.username n >root</prop>
35
Spring Framework 3
Caelum http://www.caelum.com.br
<prop
<prop
<prop
<prop
<prop
<prop
key="hibernate. connection.password"></prop>
key="hibernate. connection.autocommit">true</prop>
key="hibernate. dialect">org.hibernate.dialect.MySQLDialect</prop>
key="hibernate .show_sql">true</prop>
key="hibernate .format_sql">true</prop>
key="hibernate .hbm2ddl.auto">update</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>br.com.caelum.estoque.model.Produto</value>
</list>
</property>
</bean>
Ao integrarmos o Hibernate ao Spring, a configurao do Hibernate, que antes era feita separadamente,
passa a ser feita diretamente no Spring. Isso necessrio para deixarmos o Spring gerenciar a criaco da
SessionFactory do Hibernate permitindo assim que o Spring faa a injeco automtica da sessionFactory do
Hibernate.
destroy-method
A propriedade destroy method da tag bean o nome do mtodo que ser invocado sempre que o
Spring finalizar o ciclo de vida da nossa SessionFactory.
-
hibernate.cfg.xml
Ao invs de configurar o hibernate direto no Spring, podemos manter o arquivo hibernate.cfg.xml
separado e apenas referenci-lo no Spring:
<property name="configLocation">
<value>
classpath:location_of_config_file/hibernate.cfg.xml
</value>
</property>
5.3 - ProdutoHibernateDA0
Agora podemos criar um novo DAO que realmente acesse o banco de dados. Para isso basta pedir para
o Spring injetar uma SessionFactory em alguma classe. Lembrando que o Spring injeta apenas beans gerenciados em outros tambm gerenciados, para isso devemos declarar que nosso DAO deve ser gerenciado pelo
Spring anotando-o com @component.
Captulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - ProdutoHibernateDAO - Pgina 36
Caelum http://www.caelum.com.br
Spring Framework 3
@Component
public class ProdutoHibernateDA0 implements ProdutoDA0 {
private Session session;
@Autowired
public ProdutoHibernateDAO(SessionFactory factory) {
session = factory.openSession();
}
5A @Repository
-
Anteriormente comentamos que as anotaes wontroner e @Component tinham o mesmo objetivo, indicar
ao Spring que os beans anotados devem ser gerenciados e que a diferena entre as mesmas era apenas
semntica. Para o caso dos DAOs temos a mesma situao, podemos anotar nossos DAOs com @Repository,
ela ter o mesmo efeito da @Component.
5.5 Exerccios
-
package br.com.caelum.estoque;
@Entity
public class Produto {
@Id
@GeneratedValue
private Long id;
private String descricao;
private Integer quantidade;
//getters e setters
}
Caelum http://www.caelum.com.br
Spring Framework 3
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBeanft >
<property name="hibernateProperties"
<props>
<prop key="hibernate.connection.urr>jdbc:mysql://localhost/fj27</prop>
<prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
<prop key="hibernate.connection.username">root</prop>
<prop key="hibernate.connection.password"></prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.connection.autocommit">true</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>br.com.caelum.estoque.Produto</value>
</list>
</property>
</bean>
@Repository
public class ProdutoHibernateDAO implements ProdutoDAO {
private Session session;
@Autowired
public ProdutoHibernateDAO(SessionFactory factory) {
session = factory.openSession();
}
4) Reinicie o servidor e tente fazer o cadastro de algum produto. No funcionar. Leia a exceo e sua causa
raiz (root cause). O que aconteceu?
Caelum http://www.caelum.com.br
Spring Framework 3
5.6 @Qualifier
Ao testar o sistema o Spring lanou uma exeo na nosso console. Isso aconteceu porque o Spring descobre
o que injetar nos beans atravs do tipo do objeto a ser injetado. No nosso caso o Spring tenta injetar algum
bean que implemente a interface ProdutoDAO, porm existem duas implementaes, a ProdutoMemoriaDAO e a
ProdutoHibernateDAO, com isso o Spring se perde na hora de fazer a injeo.
Neste nosso caso poderamos simplesmente apagar a ProdutoMemoriaDA0 que resolveria o problema, porm em cenrios que realmente devam existir duas implementaes devemos especificar qual delas queremos
receber como injeo. exatamente esse o objetivo da anotao @Qualifier.
Com o @Qualifier podemos especificar atravs do id do bean qual implementao queremos receber.
public class ProdutoController {
@Autowired
Nualifier("produtoHibernateDAO")
private ProdutoDAO produtoDAO;
//cdigo antigo
}
Indicamos o nome produtoHibernateDAO porque o Spring configura o id dos beans anotados atravs do
nome da classe. A sua politica se o nome no foi passado diretamente via uma das anotaes @Controller
, @Component e @Repository ele simplesmente coloca o nome da classe anotada transformando a primeira letra
do nome em minuscula. Neste caso ProdutoHibernateDAO fica produtoHibernateDAO.
Fora a facilidade proporcionada pelo Spring, tambm tiramos proveito do uso do polimorfismo, que permitiu
uma troca tranquila da implementao.
5.7 - Exerccios
1) Altere sua classe controler para receber o ProdutoHibernateDAO ao invs do ProdutoMemoriaDA0
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
@Autowired
@Qualifier("produtoHibernateDA0")
private ProdutoDAO produtoDAO;
//cdigo antigo
}
Caelum - http://www.caelum.com.br
Spring Framework 3
Exefcidlos
1) Anote a classe ProdutoHibernateDA0 COM @Primary:
@Repository
@Primary
public class ProdutoHibernateDA0 implements ProdutoDA0{
//codigo ja existente
}
A configurao parece complicada pois dividida em duas partes e usa orientao a aspectos. No trecho <tx:advice> configurado quais mtodos sero "interceptados" pelo controle de transao. No trecho
<aop: config> habilitamos o AOP, para que ele possa cortar nossos mtodos e inserir outros comportamentos.
A configurao por anotao mais simples. Apenas precisamos indicar para o Spring quais mtodos ele
deve abrir e fechar uma transao. Isso feito usando a anotao @Transaction em um mtodo, ou mesmo
em uma classe, que tem o mesmo efeito de anotar todos os mtodos da classe. Podemos ento anotar nossos
DAO's com o @Transaction e assim ter suporte a transao.
Captulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - Exerccios - Pgina 40
ri Caelum
http://www.caelum.com.br
Spring Framework 3
@Repository
@Transactional
public class ProdutoHibernateDAO implements ProdutoDA0 {
//codigo
}
Porm at o momento nosso sistema no fez o uso de transaes. O hibernate est configurado com o
autocommit ativado, dando commit em todas nossas operaes automaticamente. Tambm temos que lembrar
que o tipo de tabela utilizada no MySQL no da suporte a transaes e por isso apenas o flush do autocommit
basta para os dados serem persistidos.
O MySQL tem dois tipos de tabela, Mylsam e InnoDB. Mylsam que a default no Ubuntu, no oferece
suporte a transaes e por isso no tivemos problemas at o momento. Para testarmos o controle de transaes
vamos alterar o tipo de tabela do MySQL, mudando o dialeto do Hibernate.
Alterar:
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
Para :
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
A segunda parte avisar que o controle de transaes ser feito via anotao, parecido com a forma de
habilitar o uso de anotaes para o Spring MVC.
<tx:annotation-driven/>
Caelum http://www.caelum.com.br
Spring Framework 3
@Repository
@Transactional
public class ProdutoHibernateDA0 implements ProdutoDAO {
private SessionFactory sessionFactory;
@Autowired
public ProdutoHibernateDAO(SessionFactory factory) {
this.sessionFactory = factory;
}
//outros metodos
}
5-1 ; EEm?nclicicns
1) Altere o arquivo app conf ig xml para que o dialeto do MySQL seja InnoDB e com isso tenhamos suporte s
transaes com o banco de dados manualmente:
-
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
3)
mysql -u root
use fj27;
select * from Produto;
Tabelas antigas
Se o produto foi inserido, provavelmente porque a tabela ainda est no formato MyISAM, mude-a
para InnoDB com o comando: alter table Produto type=innodb;
Caelum http://www.caelum.com.br
Spring Framework 3
@Repository
@Transactional
public class ProdutoHibernateDA0 implements ProdutoDAO {
//codigo
}
editar()
e alterar0 .
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
//cdigo antigo
@RequestMapping(value="/alterar" , method=RequestMethod.POST)
public String alterar(Produto produto) {
produtoDAO.alterar(produto);
return "redirect:/produtos/listar.html";
}
@RequestMapping(value="/editar", method=RequestMethod.GET)
public ModelAndView editar(Long id) {
Produto produto = produtoDAO.buscarPorld(id);
ModelAndView modelAndView = new ModelAndView("produtos/editar");
modelAndView.addObject(produto);
return modelAndView;
}
Caelum http://www.caelum.com.br
Spring Framework 3
</head>
<body>
<cforEach items="${produtoList}" yar="produto">
${produto.descricao} - ${produto.quantidade} <a href="/estoque/produtos/mostrara{produto.id}.html">detalhes</a>
- <a href="/estoque/produtos/editar.html?id=${produto.id}">editar</a> <br/>
</c:forEach>
</body>
</html>
1) Para deixar o sistema mais completo, desenvolva a funcionalidade de remoo dos produtos.
5.17 - Entitidade
Devido ao nosso novo problema vamos adicionar ao nosso projeto a entidade de movimentacao. Ele vai
representar as movimentaes realizadas. Entre seus atributos temos a data que foi efetuada, a quantidade do
produto que foi alterada, o tipo da movimentacao que pode ser ENTRADA ou SAIDA e um relacionamento ao
Produto em questo.
Captulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - Exerccio Opcional: Remoo de produtos - Pgina 44
Caelum http://www.caelum.com.br
Spring Framework 3
package br.com.caelum.estoque;
@Entity
public class Movimentacao {
@Id
@GeneratedValue
private Long id;
private Calendar data;
@Enumerated(EnumType.STRING)
private TipoMovimentacao tipo;
private Integer quantidade;
@ManyToOne
private Produto produto;
//getters e setters
}
Porm no basta apenas termos esta nova entidade, precisamos alterar a lgica de alterao de produtos.
Nesta nova lgica ao alterar a quantidade de um produto, calcularemos se a movimentao de sada ou
entrada e a quantidade movimentada. Para isso isolaremos esta lgica em um novo cdigo:
@Service
public class GeradorDeMovimentacao {
private final ProdutoDAO produtoDAO;
@Autowired
public GeradorDeMovimentacao(@Qualifier("produtoHibernateDA0")ProdutoDA0 produtoDAO) {
this.produtoDAO = produtoDAO;
Basicamente o que o mtodo geraMovimentacao faz receber um produto com a quantidade nova de itens
e comparar com a atual, descobrindo se a movimentao de entrada ou sada.
Captulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - Entitidade - Pgina 45
Spring Framework 3
Caelum http://www.caelum.com.br
alterar
@RequestMapping(value="/alterar" , method=RequestMethod.POST)
public String alterar(Produto produto) {
Movimentacao movimentacao = geradorDeMovimentacao.geraMovimentacao(produto);
movimentacaoDao.salvar(movimentacao);
produtoDAO.alterar(produto);
return "redirect:/produtos/listar.html";
}
Agora toda as vezes que alterarmos a quantidade de um produto, ser gerada uma movimentao.
5.18 Exerccios
1) Crie a entidade Movimentacao e a classe
registrar a entidade no xml do Spring:
TipoMovimentacao,
package br.com.caelum.estoque;
@Entity
public class Movimentacao {
@Id
@GeneratedValue
private Long id;
private Calendar data;
@Enumerated(EnumType.STRING)
private TipoMovimentacao tipo;
private Integer quantidade;
@ManyToOne
private Produto produto;
//getters e setters
}
package br.com.caelum.estoque;
public enum TipoMovimentacao
ENTRADA, SAIDA
Ao alterar a interface precisamos implementar este novo mtodo nas classes ProdutoHibernateDAO e
ProdutoMemoriaDAO.
Caelumhttp://www.caelum.com.br
Spring Framework 3
@Repository
Caelum http://www.caelum.com.br
Spring Framework 3
package br.com.caelum.estoque.controller;
@Service
public class GeradorDeMovimentacao {
private final ProdutoDA0 produtoDAO;
@Autowired
public GeradorDeMovimentacao(@Qualifier("produtoHibernateDA0")ProdutoDA0 produtoDAO) {
this.produtoDAO = produtoDAO;
}
mov.setQuantidade(Math.abs(produto.getQuantidade()-quantidadeAtual));
return mov;
}
5) Por fim, altere o mtodo alterar do controller, ProdutoController, no esquea de declarar as novas
dependncias para esta classe funcionar e tornar o mtodo alterar transacional:
package br.com.caelum.estoque.controller;
@Controller
@RequestMapping(value = "/produtos")
public class ProdutosController {
@Autowired
@Qualifier("produtoHibernateDAO")
private ProdutoDAO produtoDAO;
@Autowired
private GeradorDeMovimentacao geradorDeMovimentacao;
@Autowired
private MovimentacaoDA0 movimentacaoDao;
@RequestMapping(value="/alterar" , method=RequestMethod.POST)
@Transactional
public String alterar(Produto produto) {
Movimentacao movimentacao = geradorDeMovimentacao.geraMovimentacao(produto);
movimentacaoDao.salvar(movimentacao);
produtoDAO.alterar(produto);
return "redirect:/produtos/listar.html";
}
6) Teste alterar um produto j existente e confira no banco se a movimentao foi gravada com sucesso.
Para entrar no mysql, v ao terminar e digite:
Captulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - Exerccios - Pgina 48
Caelum http://www.caelum.com.br
Spring Framework 3
mysql -u root
use fj27 .
select * from Movimentacao;
cional:
@Entity
public class Produto {
@Id
@GeneratedValue
private Long id;
private String descricao;
private Integer quantidade;
@OneToMany(mappedBy="produto")
private List<Movimentacao> movimentacoes;
5.20 - Exerccios
1) Modifique a classe Produto para ter um relacionamento bidirecional com a classe Movimentacao:
Captulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - Mostrando as movimentaes na tela - Pgina 49
Spring Framework 3
Caelum http://www.caelum.com.br
</body>
/WEB-
INF/views/produtos/mostrar.jsp:
<h2>Lista de Movimentaes</h2>
<ul>
<cforEach items="${produto.movimentacoes}" var="m">
<li>
${m.tipo} - ${m.quantidade} <fmt:formatDate value="${m.data.time}" pattern="dd/MM/yyyy hh:mm"/>
</li>
</c:forEach>
</ul>
3) Teste entrar na tela de detalhes de um produto que tenha movimentaes, deu certo?
O esperado neste exerccio que d a exception LazylnitializationException, mas qual o motivo para ela
ter ocorrido?
Ao usar o gerenciamento de transao do Spring, cada operao que precisar de uma transao, acaba
abrindo e fechando uma sesso tambm e aqui que est o problema. A sesso que o produto estava associado ser fechada ao final do mtodo do controller (ou na DAO) que buscou o produto, com a sesso fechada o
hibernate no consegue mais carregar (de maneira lazy) a lista de movimentao.
Nosso cdigo propriamente dito no usa a lista de movimentaes nenhuma vez, porm esta lista lida
pelo jsp, na Expression Language ${produto.movimentacoes}. Na etapa de renderizao do JSP a sesso do
hibernate j foi encerrada, lanando uma LazylnitializationException no JSP.
O que fazer para corrigir este problema?
Capitulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - Lidando com LazylnitializationException - Pgina 50
Caelum http://www.caelum.com.br
Spring Framework 3
5.23 Exerccios
1) Declare o filtro do Spring no seu web.xml
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
</filter-mapping>
2) O Filtro do Spring procura por um bean chamado sessionFactory para abrir uma sesso, porm ele no
procura na servlet do Spring e sim em um contexto ativado pelo ContextLoaderListener do Spring, registre-o
tambm no web.xml.
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3) O ContextLoaderListener precisa saber onde est o arquivo de configurao do Spring (por padro ele
procura pelo applicationContext. xmi dentro de WEB-INF).
Registre um context-param abaixo da tag
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/app-config.xml</param-value>
</context-param>
4) Para evitar o carregamento repetido das configuraes do Spring vamos fazer a Servlet do Spring carregar
no mais o app config. xmi, mas sim um arquivo de configuraes vazio.
-
Crie um arquivo chamado empty.xml dentro da pasta /WEB-INF/spring com o seguinte contedo:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
xsi:schemaLocation="
Captulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - Soluo Open Session in View - Pgina 51
Caelum http://www.caelum.com.br
Spring Framework 3
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xscr >
</beans>
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/empty.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Propagao
Ao trabalharmos com transaes o Spring nos fornece seis tipos de propagao. A propagao ocorre
quando uma lgica de negcios que utiliza uma transao chama outra que pode ou no utilizar transaes.
A escolha do tipo de propagao permite criar novas transaes, parar a transao atual, no aceitar a
chamada se uma transao j est acontecendo, etc.
REQUIRED
REQUIRES_NEW
MAN DATORY
NESTED
NEVER
Captulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - Transaes Avanadas - Pgina 52
Caelum http://www.caelum.com.br
Spring Framework 3
NOT SUPPORTED
SUPPORTS
Isolamento
Uma transao pode ser isolada das outras que esto ocorrendo.
ser utilizadas:
As constantes da interface
ISOLATION_READ_UNCOMMITTED - permite o que chamamos de leitura suja (dirty read), isto , voc
pode visualizar alteraes em outras linhas que esto participando de uma outra transao nesse instante
e se algum rollback for executado na outra transao, sua leitura ter sido invlida.
ISOLATION_READ_COMMITTED - proibe o dirty read fazendo com que os dados lidos sejam os anteriores a alterao da outra transao em andamento.
ISOLATION_REPEATABLE_READ - no permite que ocorra a situao onde voc leia uma linha de dados,
essa linha seja alterada por outra transao e ento voc leia novamente aquela linha. Se permitisse, voc
receberia dessa vez dados diferentes da primeira leitura (non-repeatable read).
ISOLATION SERIALIZABLE - implica nas mesmas limitaes de ISOLATION_REPEATABLE_READ e
no permite a situao onde feita uma pesquisa com clusula WHERE pela sua transao, outra transao insere uma linha que seria retornada na clusula WHERE que voc utilizou e uma nova pesquisa
com a mesma clusula WHERE retornaria o novo item (phantom read).
Caelum http://wwwcaelum.com.br
Spring Framework 3
class Teste{
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
} ;
Embora o cdigo ainda seja procedural e braal, este cdigo mal se preocupa com exceptions, transaes
ou conexes todo o trabalho feito pelo JdbcTemplate.
Captulo 5 - Integrando Spring, Hibernate e tecnologias de Persistncia - Para saber mais: HibernateTemplate e outros Templates Pgina 54
CAPTULO
Assim podemos injeta-la em qualquer outro bean que tambm gerenciado pelo Spring.
55
Spring Framework 3
Caelum http://www.caelum.com.br
@Service
Atravs do validator chamar o mtodo validate() e o mesmo nos retorna uma lista de
ConstraintViolation representando as validaes violadas. O Spring 3 adiciona ao seu modlo MVC um
forma declarativa de utilizarmos a integrao com a Bean Validation. A anotao @Valid nos permite indicar
que deve se validatar um paramtro de um controller de acordo com as anotaes declaradas no objeto.
@Controller
A anotao @Valid informa ao Spring MVC para chamar o mtodo validate() do Validator para o paramtro pessoa. @Valid ativada atravs da configurao:
<mvc:annotation-drivenh
6A - Exerccios
Produto.
@Entity
Caelum http://www.caelum.com.br
Spring Framework 3
@Entity
public class Movimentacao {
@Id
@GeneratedValue
private Long id;
@NotNull
private Calendar data;
@Enumerated(EnumType.STRING)
private TipoDeMovivementacao tipo;
@NotNull
private Integer quantidade;
@ManyToOne
@NotNull
private Produto produto;
}
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
// cdigo antigo
@RequestMapping(value="/salvar" , method = RequestMethod.POST)
@Transactional
public String salvar(@Valid Produto produto) {
produtoDAO.salvar(produto);
geraMovimentacao.entrada(produto);
return "redirect:/produtos/listar.html";
}
// cdigo antigo
}
Spring Framework 3
Caelum http://www.caelum.com.br
BindingResult o objeto que tem a analise do que aconteceu na validao, podemos pedir esse objeto para o
Spring apenas declarando o mesmo como paramtro de um mtodo de controller.
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
//...
@RequestMapping(value="/salvar", method=RequestMethod.POST)
Com ele podemos verificar a existncia de erros e redirecionar para uma pgina de erros antes de da
tentativa de salvar o objeto e ter as excees devolvidas ao usurio.
@Controller
@RequestMapping(value="/produtos")
public class ProdutosController {
//...
@RequestMapping(value="/salvar", method=RequestMethod.POST)
Retornando para a o formulario de cadastro podemos utilizar das taglibs do Spring para mostramos os erros
ao usurio.
6.6 - Exerccios
1) Altere o mtodo salvar() de ProdutosController para redirecionar no caso de erros.
produtoDAO.salvar(produto);
geraMovimentacao.entrada(produto);
return "redirect:/produtos/listar.html";
}
Caelum http://www.caelum.com.br
Spring Framework 3
@RequestMapping(value="/form" , method=RequestMethod.GET)
public String form(Model model) {
model.addAttribute(new Produto());
return "produtos/form";
}
produtoDAO.alterar(produto);
geraMovimentacao.entrada(produto);
return "redirect:/produtos/listar.html";
}
Spring Framework 3
Caelum http://www.caelum.com.br
6.8 - Exerccio
1) Crie a pasta Mn dentro de WEB INF e nela crie o arquivo messages.properties adicionando o seguinte
conteudo.
-
CAPTULO
Spring Security
7.1 - Introduo ao Spring Security
Spring tambm oferece uma alternativa ao padro Java na segurana de aplicaes de negcios. Usando o
padro Java, JAAS (Java Authentication and Authorization Service), as aplicaes no so mais 100% portvel
entre servidores de aplicaes. JAAS sempre depende de uma configurao especifica do servidor. Alm da
desvantagem, JAAS tem a fama de ser complicado e burocrtico demais.
Spring Security uma alternativa flexivel, simples e 100% portvel entre servidores. Ele nem precisa de um
servidor de aplicao, pode ser usado dentro de uma aplicao standalone ou dentro de um servlet container.
Spring Acegi
Spring Security tambm conhecido como Acegi. Spring Security o novo nome e foi introduzido
na verso 2.0.
Para habilitarmos o Spring Secutiry precisamos fazer algumas configuraes no web.xml do nosso servlet
container. Entre as configuraes o mais importante o filtro DelegatingFilterProxy que vai interceptar as
chamadas a aplicao e verificar as retries que vamos configurar utilizando o Spring Secutiry. A configurao
do Filtro bem simples, mas importante dizer exatamente onde o mesmo vai atuar com a url pattern do
mapeamento.
-
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
O Filtro toda a base do Spring Security para a web. Mas tambm precisamos de uma outra configurao.
O filtro requer um listener para disponibilizar acesso ao WebApplicationContext que onde ficam os objetos
gerenciados pelo o Spring. O listener deve ser configurado em conjunto com um paramtro que informa a
localidade do XML de configurao do Spring.
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
61
Spring Framework 3
Caelum http://www.caelum.com.br
<param-value>/WEB-INF/spring/app-config.xml</param-value>
</context-param>
1) Para utilizarmos o Spring Secutiry precisamos definir um listener e um filter no web.xml. Ambos so os
responsaveis por validar as estruturas de segurana que definirmos no XML do spring. O Filter mapeado
para interceptar todas as uris do projeto. E o listener precisa do parmtro contextConfigLocation que informa
onde o XML do Spring est.
<webapp>
/...
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter -class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</webapp>
Todo sistema que requer autenticaco esperam que um usuario posso se cadastrar sem a intervero do
time de densevolvimento e que o mesmo posso ter suas permisses atribuidas tambm dinamicamente. O
Spring Secutiry nos permite utilizar um modelo de dados prprio para o controle da seguraa de nossa aplicao.
E por isso vamos definir as classes de domnio usuario e Grupo que vo representar o ACL do nosso sistema.
Ambas as classes so Entidades, e por isso faremoz o mapeamento de objeto relacional utilizando as
anotaes do JPA.
A classe Grupo representa a ROLE de alguem no sistema, ou seja um usuario pode ter a ROLE de Administrador ou de Usuario que permite acesso a elementos diferentes dentro da aplicao. A Role representada
pelo nome da entidade Grupo.
Captulo 7 - Spring Security - Exerccios - Configurando o Spring Security - Pgina 62
Caelum http://www.caelum.com.br
Spring Framework 3
package br.com.caelum.estoque;
@Entity
public class Grupo {
@Id
@GeneratedValue
private Long id;
private String nome;
//getters e setters
}
A classe Usurio representa o login de alguem no sistema o mesmo possui login e senha de acesso. E uma
lista das roles que o mesmo pode executar nos elementos do sitema.
package br.com.caelum.estoque;
@Entity
public class Usuario {
@Id
@GeneratedValue
private Long id;
private String login;
private String senha;
@OneToMany(fetch=FetchType EAGER)
private List<Grupo> grupos;
// getters e setters
}
package br.com.caelum.estoque;
@Entity
public class Usuario {
@Id
@GeneratedValue
private Long id;
private String login;
private String senha;
@OneToMany(fetch=FetchType EAGER)
private List<Grupo> grupos;
// getters e setters
}
Spring Framework 3
Caelum http://www.caelum.com.br
2) Temos que implementar e mapear a classe Grupo, tambm. Est classe deve estar no pacote
br.com.caelum.estoque.
package br.com.caelum.estoque;
@Entity
public class Grupo {
@Id
@GeneratedValue
private Long id;
private String nome;
//getters e setters
}
3) necessrio configurar o XML do spring, informando o Hibernate sobre as novas entidades mapeadas.
<property name="annotatedClassesu >
<list>
<value>br.com.caelum.estoque.Grupo</value>
<value>br.com.caelum.estoque.Usuario</value>
</list>
</property>
O mtodo getAuthority () delega para o getNome () para retornar o nome da ROLE. O nome das ROLES
devem conhecedir com as definidas no XML do Spring. Mais adiante veremos isso.
Captulo 7 - Spring Security - Interfaces de Acesso - Pgina 64
Caelumhttp://www.caelum.com.br
Spring Framework 3
A interface UserDetails deve ser implementada pela classe que representa as informaes do usurio. Para
quem implementa est interface requerido a sobrescrita de 7 mtodos, todos informando algum aspecto de
segurauna.
O mtodo getUsername() deve retornar o nome do usurio que est logado. Em nossa entidade Usuario
mantemos o username na propriedade login da entidade. Por isso delegariamos a chamada desse mtodo para
getLogin().
O mtodo getPassword() deve retornar a senha do usurio que est logado. Em nossa entidade Usuario
mantemos o password na propriedade senha da entidade. Por isso delegariamos a chamada desse mtodo
para getSenhaO
.
O mtodo isAccountNonExpired() retorna true OU false. E pode ser utilizado para uma possvel regra que
determina um tempo de experirao para o usurio cadastado.
O mtodo isAccountNonLocked() retorna true OU false. E pode ser utilizado para uma possvel regra que
bloqueia a conta de usurio, por uma indisplicncia ou algo similiar.
O mtodo isCredentialsNonExpired() retorna true Ou false. E pode ser utilizada para uma regra de
crendenciais para usurios. Por exemplo uma credencial provendo o beneficio de acessar algo a mais no
sistema.
O mtodo isEnabled() retorna true ou false. E pode ser utilizada para determinar se a conta do usurio
est ativa ou no.
A implementao da nossa classe usurio vai retornar true para todos os mtodos para simplificar o desenvolvimento.
Captulo 7 - Spring Security - Interfaces de Acesso - Pgina 65
Spring Framework 3
Caelum http://www.caelum.com.br
@Entity
public class Usuario implements UserDetails {
@Id
@GeneratedValue
private Long id;
private String login;
private String senha;
@OneToMany(fetch=FetchType.EAGER)
private List<Grupo> grupos;
//getters e setters
@Override
public Collection<GrantedAuthority> getAuthorities() {
return new ArrayList<GrantedAuthority>(grupos);
}
@Override
public String getPassword() {
return getSenha();
}
@Override
public String getUsername() {
return getLogin();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
Essas interfaces acima so utilizadas nas entidades que definimos para o nosso domnio. Mas o Spring Secutiry no consegue automaticamente fazer uma consulta para verificar o login de um usurio. Ele precisa tambm que informemos a ele a consulta que deve ser chamada para verificar a autenticao de um usurio. Para
isso usamos a interface UserDetailsService que requer que implementemos o mtodo ioaduserByusername ()
que recebe como paramtro o nome do usurio que est efetuando o login. Na assinatura desse mtodo ele
devolve um objeto do tipo UserDetails. Ou seja devemos fazer a consulta baseada no nome do usurio e retornar uma instancia de usurio do nosso domnio que o mesmo implementa a interface UserDetails. Apesar
do Spring apenas nos fornecer o nome do usurio para a consulta, ele faz o teste da senha com o objeto retorCaptulo 7 - Spring Security - Interfaces de Acesso - Pgina 66
Caelum http://www.caelum.com.br
Spring Framework 3
nado ento se a consulta retorna um objeto com uma senha diferente da passada o Spring vai invalidar o login.
Vamos fazer nossa classe DAO representar o tipo UserDetailsService.
package br.com.caelum.estoque.dao;
public interface UsuarioDAO extends UserDetailsService {
package br.com.caelum.estoque.dao;
@Repository
public class UsuarioHibernateDAO implements UsuarioDAO {
private HibernateTemplate hibernateTemplate;
@Autowired
public UsuarioHibernateDAO (SessionFactory factory) {
hibernateTemplate = new HibernateTemplate(factory);
}
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
List usuarios =
hibernateTemplate.find("from Usuario where login = ?", username);
if(!usuarios.isEmpty()) {
return (Usuario) usuarios.get(0);
}
return null;
}
O ltimo passo na jornada de fazer o Spring Security utilizar nosso prprio modelo de objetos indicar tudo
que criamos a ele. Precisamos configurar nossa classe UsuarioHibernateDAO como o provider de segurana, e
isso possvel porque o mesmo implementa a interface UserDetailsService.
<sec:authentication-manager>
<sec:authentication-provider user-service-ref="usuarioHibernateDAO"/>
</sec:authentication-manager>
1) Nossa entidade Grupo deve implementar a interface GrantedAuthority. Est interface requer que implementemos 1 mtodo informando o nome da ROLE que o spring deve validar.
Spring Framework 3
Caelum http://www.caelum.com.br
@Entity
public class Grupo implementa GrantedAuthority {
@Id
@GeneratedValue
private Long id;
private String nome;
//getters e setters
00verride
public String getAuthority() {
return getNome();
}
2) Quando utilizamos o Spring Security acessando o banco de dados, o Spring requer que implementemos algumas interfaces pre-definidas nos objetos de domnio que representam a segurana. Para nossa Entidade
usuario vamos implementar a interface UserDetails do Spring. Est interface requer que reimplementamos
7 mtodos utilizados para segurana.
Caelum http://www.caelum.com.br
Spring Framework 3
@Entity
public class Usuario implements UserDetails {
@Id
@GeneratedValue
private Long id;
private String login;
private String senha;
@OneToMany(fetch=FetchType.EAGER)
private List<Grupo> grupos;
//getters e setters
@Override
public Collection<GrantedAuthority> getAuthorities() {
return new ArrayList<GrantedAuthority>(grupos);
}
@Override
public String getPassword() {
return getSenha();
}
@Override
public String getUsername() {
return getLogin();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
3) Para o acesso a dados vamos definir um DAO para Usuario. Seguinte a mesma boa prtica de programar
orientado a interface vamos definir uma interface UsuarioDAO. Vamos definir tambm que nossa interface
extenda a interface UserDetailsService do spring assim o DAO que implementar UsuarioDAO pode ser
utilizado como um provider de segurana no Spring.
Spring Framework 3
Caelum http://www.caelum.com.br
package br.com.caelum.estoque.dao;
public interface UsuarioDAO extends UserDetailsService {
}
4) Vamos definir a implementacao do DAO para a interface UsuarioDAO. Precisamos prover uma implementao
para o mtodo loadUserByUsername () que herdamos da interface UserDetailsService, faa com que a
interface UsuarioDAO herde da interface UserDetailsService. O mtodo loadUserByUsername chamado
pelo Spring Secutiry para a autenticao de um usuario no banco de dados. Nossa implementao recebe
tambm a factory do Hibernate e faz o uso de HibernateTemplate por causa de transaes e exceptions.
package br.com.caelum.estoque.dao;
@Repository
public class UsuarioHibernateDAO implements UsuarioDAO {
private HibernateTemplate hibernateTemplate;
@Autowired
public UsuarioHibernateDAO (SessionFactory factory) {
hibernateTemplate = new HibernateTemplate(factory);
}
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
List usuarios =
hibernateTemplate.find("from Usuario where login = ?", username);
if(!usuarios.isEmpty()) {
return (Usuario) usuarios.get(0);
}
return null;
}
5) Vamos configurar o XML do Spring app-config.xml e informar que o provider de autenticao o nosso
DAO UsuarioHibernateDAO. Com essa configurao o Spring Security vai fazer a chamada ao mtodo
loadUserByUsername() para validar o login de um suario.
<security:authentication-manager>
<sec:authentication-provider user-service-ref="usuarioHibernateDA0"/>
</security:authentication-manager>
7.7 - Login
Vamos criar a pgina de login para nosso sistema. A mesma deve seguir 3 convenes definidas pelo o
Spring Secutiry. Os inputs que definem o nome e senha do usurio devem ter os respectivos nomes j username
e j password. E a url defina no action do form deve bater com a configurada no XML do Spring. Nossa jsp ficaria
assim:
<html>
<head>
Captulo 7 - Spring Security - Login - Pgina 70
Caelum http://www.caelum.com.br
Spring Framework 3
<title>Login</title>
</head>
<body>
<form action="/estoque/login" method="post">
Login : <input type="text" name="j_username"/><br/>
Senha : <input type="text" name="j_password"/><br/>
<input type="submit" value="Login">
</form>
</body>
</html>
Bem simples. A configurao principal dessa parte realizada no XML do Spring. Devemos definir as
ROLES que nossos usurios podem ter e quais lugares as mesmas tem acesso. Devemos definir a url que
a pgina de login deve chamar, e para onde o Spring tem que redirecionar caso alguem tente acessar algo
que no possa. Tambm devemos informar para -onde realizado o redirecionamento em caso do login com
sucesso. Todas essas configuraes so realizadas no elemento http do Spring Security.
<sec:http auto-config="true">
</sec:http>
Em nosso sistema temos uma ROLE a de usurio, que define acesso para as funcionalidades do
ProdutoController. Na configurao do Spring informamos um tipo de URL que deve ser interceptado pelo
Security e o nome da ROLE, ou seja, do Grupo que pode acessar aquele tipo de url. Para definirmos que apenas Usurios cadastrados no grupo ROLE USER podem ter acesso as funcionalidades de ProdutoController
definimos:
<sec:http auto-config="true">
<sec:intercept-url pattern="/produto/**" access="ROLE_USER" 1>
</sec:http>
Vamos definir no elemento http qual url deve ser chamada para a validao de segurana. Por exemplo,
para termos a segurana do Spring executada ao chamar a url /login no sistema. Configuramos o atributo
login-processing-url no elemento form-login de segurana. Configuramos tambm os atributos login-page que
indica para onde deve ser redirecionado caso alguem tente acessar um recurso sem estar logado ou sem ter
permisso, default-target-url para definir o alvo, ou seja a jsp que deve ser redirecinado no caso de login com
sucesso e always-use-default-target para indicar que a todo login o redirecionamento para o default-target-url.
<sec:http auto-config="true">
<sec:intercept-url pattern="/produto/**" access="ROLE_USER" 1>
<sec:form-login login-page="/login.html" always-use-default-target="true"
default-target-url="/produto/listar.html" login-processing-url="/login"/>
<sec:logout logout-url="/logout" logout-success-url="/login.html" 1>
</sec:http>
Spring Framework 3
Caelum http://www.caelum.com.br
<head>
<title>Login</title>
</head>
<body>
<form action="/estoque/login" method="post">
Login : <input type="text" name="j_username"/><br/>
Senha : <input type="password" name="j_password"/><br/>
<input type="submit" value="Login">
</form>
</body>
</html>
2) Como ns seguimos a boa prtica de colocar as jsps abaixo do diretrio "WEB INF" o usurio no tem
acesso a nenhuma jsp sem a passagem por um Contmller do Spring. Por isso vamos criar a classe
usuarioscontroner e cnfigurar uma url de acesso para nossa pgina de login.
-
package br.com.caelum.estoque.controller;
@Controller
public class UsuariosController {
@RequestMapping(value="/login")
public String index() {
return "usuarios/login";
}
3) Todas as classes de infraestura para o controle da segurana de nossa aplicao ja foram definidas. Agora
precisamos configurar o Spring as permisses, url de login, pgina de login, url de logout, oque fazer se um
usurio invalido tentar se logar. Essas definies so feitas no XML do Spring app-config.xml.
<security:http auto-config="true">
<security:intercept-url pattern="/produtos/**" access="ROLE_USER" />
<security:form-login login-page="/login.html" always-use-default-target="true"
default-target-url="/produtos/listar.html" login-processing-url="/login"/>
<security:logout logout-url="/logout" logout-success-url="/login.html" />
</security:http>
4) Para testarmos precisamos de um usurio no banco de dados. Executem o Script abaixo para no mysql.
insert into Usuario (login,senha) values ('eu','eu');
insert into Grupo (nome) values ('ROLE_USER');
insert into Usuario_Grupo values (1,1);
5) Inicie o Servidor. E chame acesse a url da pgina de login http: localhost:8080 estoque login.html . Tente
se logar com um login e senha que no exitem no Banco de Dados, e verifique que no console do eclipse
que o Spring executa o mtodo loadUserByUsername() e o hibernate imprime a consulta. Devido ao fato do
usurio no existir o Spring redireciona o fluxo para a mesma pgina de login.
6) Vamos fazer um segundo teste agora. Ainda sem se logar tente acessar uma das funciolidades definidas em
ProdutosController. Por exemplo a listagem http://localhost:8080/estoque, produtos Aistar.html ou o caCaptulo 7 - Spring Security - Exerccios - Logando no Sistema - Pgina 72
Caelum http://www.caelum.com.br
Spring Framework 3
Caelum http://www.caelum.com.br
Spring Framework 3
CAPTULO
Provrbio Chins
75
Caelum http://www.caelum.com.br
Spring Framework 3
package br.com.caelum.estoque.service;
@Service
public class GeraMovimentacao {
private final MovimentacaoRepository movimentacaoRepository;
private Logger logger = Logger.getLogger(GeraMovimentacao.class);
@Autowired
public GeraMovimentacao(MovimentacaoRepository movimentacaoRepository) {
this.movimentacaoRepository = movimentacaoRepository;
}
return quantidade;
}
Este cdigo de log um clssico cdigo que estar presente em diversos pontos do sistema. Cdigo
espalhado pelo sistema todo caracteriza um aspecto, desta forma, ao invz de ter esse cdigo direto na classe
GerenciadorProjeto e diversas outras, poderamos isolar este cdigo em um aspecto que realiza esta tarefa.
Captulo 8 - Apndice - AOP - Programao Orientada a Aspectos - AOP na prtica - Pgina 76
Caelum http://www.caelum.com.br
Spring Framework 3
Assim, dexamos o cdigo relacionado a logging (requisito ortogonal, infra estrutura) separado da nossa
classe de negcio.
Alguns outros exemplos comuns de aspectos incluem o controle transacional, auditoria, autorizao e cache
automtico de resultados.
exemplo mais clssico a execuo de um mtodo, porm blocos de tratamento de excesses (try-catch)
tambm poderiam ser considerados como join points.
Advice quem define o comportamento tomado pelo aspecto em um determinado join point. Sempre
escrevemos o cdigo dos aspectos dentro de Advices.
Pointcut um conjunto de join points a ser alvo de um particular Advice. Cada Advice deve ser associado
a um Pointcut. Geralmente, os pointcuts so implementados como expresses regulares, mas poderiam
Spring Framework 3
Caelum http://www.caelum.com.br
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop -2.5.xsd">
-->
<!-</beans>
Perceba que o namespace xmlns:aop, bem como seu xsd spring-aop-2.5.xsd foram adicionados. Basta
agora habilitar o suporte a AOP. Para tal, existe a tag <aop:aspectj autoproxy/>:
-
-->
<aop:aspectj-autoproxy/>
</beans>
Os aspectos so simples componentes do Spring. Como j estamos usando anotaes, basta criar um novo
componente que ser o nosso aspecto de logging:
import org.springframework.stereotype.Component;
@Component
public class LogAspect {
}
Para transformar este componente em um aspecto, basta agora anot-lo com @Aspect. Esta anotao vem
do AspectJ.
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
}
Caelum - http://www.caelum.com.br
Spring Framework 3
2) Crie a classe de aspecto LoggingAspect no pacote br.. com. caelum. estoque. aop:
package br.com.caelum.estoque.aop;
@Aspect
@Component
public class LoggingAspect {
}
A sintaxe para definio de pointcuts do AspectJ exige a definio de um tipo de pointcut. O tipo mais
comum (e recomendado na maioria dos casos) o execution, que diz respeito a join points que so execuo
de mtodos. Os outros tipos possveis so:
execution(expr): inclui join points que sejam execuo de mtodos;
Captulo 8 - Apndice - AOP - Programao Orientada a Aspectos - Definindo Pointcuts e Advices - Pgina 79
Caelum http://www.caelum.com.br
Spring Framework 3
@within(expr): inclui todos os join points que sejam de tipos que contenham determinada anotao
(implementa um interface anotada, por exemplo);
@annotation (expr) : inclui todos os join points anotados com determinada anotao;
bean(expr): inclui todos os join points de beans que tenham determinado nome. Este tipo de pointcut
As opes que tem o '?' so opcionais, alm disso todas elas podem receber o caracter coringa -' em
qualquer parte. O padro para os parmetros diferente e funciona da seguinte forma:
O: nenhum parmetro
(..): quaisquer parmetros (zero ou mais)
(*): apenas um parmetro de qualquer tipo
(*,Long,String): um de qualquer tipo, um Long e outro String, na ordem
Seguindo as regras acima, podemos criar pointcuts que representem pontos do sistema para aspectos
serem aplicados, como por exemplo todos os mtodos de gerenciadores:
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* br.com.caelum.estoque.service..*.*(..))")
public void metodosDeServico() {}
}
Captulo 8 - Apndice - AOP - Programao Orientada a Aspectos - Definindo Pointcuts e Advices - Pgina 80
Caelum http://www.caelum.com.br
Spring Framework 3
Neste caso, todos os join points de mtodos dentro de classes que estejam no pacote
br.com.caelum.estoque.service e subpacotes (note o entram para este pointcut definido com o nome
de metodosDeServico ().
Agora que temos o pointcut necessrio, poderemos criar um advice dentro deste aspecto, relativo a
este pointcut. Para cada tipo de advice, existe uma anotao equivalente (@Before, @AfterReturning,
@AfterThrowing, @AI ter - finnaly - e @Around).
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* br.com.caelum.estoque.service..*.*(..))")
public void metodosDeServico() {}
@Before("metodosDeServico()")
public void logalloComeco() {}
}
Devemos sempre dizer qual (ou quais) pointcut(s) est(o) relacionado(s) ao advice que estamos criando.
Repare que o argumento da anotao o nome do pointcut associado.
Os mtodos advice podem ainda opcionalmente receber um objeto do tipo JoinPoint que representa o join
point onde o aspecto est sendo aplicado. Este join point guarda informaes valiosas para o nosso advice de
logging:
@Aspect
@Component
public class LoggingAspect {
@Before("metodosDeServico()")
public void logalloComeco(JoinPoint joinPoint) {
Logger logger = Logger.getLogger(joinPoint.getTarget().getClass());
String methodName = joinPoint.getSignature().getName();
String typeName joinPoint.getSignature().getDeclaringTypeName();
logger.info("Executando metodo: " + methodName + " da classe: " + typeName);
}
@Pointcuteexecution(* br.com.caelum.estoque.service..*.*(..))")
public void metodosDeServico() {}
}
8.8 Exerccios
Caelum http://www.caelum.com.br
Spring Framework 3
@Aspect
@Component
public class LoggingAspect {
//Log4j
private Logger logger = Logger.getLogger(LoggingAspect.class);
@Before("metodosDeServico()")
public void logalloComeco(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
String typeName joinPoint.getSignature().getDeclaringTypeName();
logger.info("Executando metodo: " + methodName + " da classe: " + typeName);
}
@Pointcut("execution(* br.com.caelum.estoque.service..*.*(..))")
public void metodosDeServico() {}
}
2) Para o Logger funcionar precisamos configurar o Iog4j para ele saber que deve logar nossa classe. Devemos
adicionar configurar o arquivo log4j.properties que se encontra no nosso diretorio "src" com a seguinte linha:
log4j.logger.br.com.caelum.estoque.aop=info
3) Start o tomcat e cadastre um ou dois produtos. Verifique ento que no console vo ter as logadas as
informaes referente a execuo do mtodo entrade().
@AfterReturning(pointcut="metodosDeGerenciadores()")
public void loga0Resultado(JoinPoint joinPoint) {
}
@Pointcut("execution(* br.com.caelum.fj27.gerenciador..*.*(..))")
public void metodosDeGerenciadores() {}
}
Caelum - http://www.caelum.com.br
Spring Framework 3
A anotao @AfterReturning serve para definir um advice que executado logo aps o trmino dos join
points. Para fazer o log do resultado, precisamos de alguma maneira conseguir capturar o resultado da execuo
do join point.
O interessante que o advice pode receber um parmetro que representa o retorno do join point. Dizemos
qual dos parametros representa o retorno, atravs da prpria anotao @AfterReturning.
@Aspect
@Component
public class LoggingAspect {
@Before("metodosDeGerenciadores()")
public void logalloComeco(JoinPoint joinPoint) {
Logger logger = Logger.getLogger(joinPoint.getTarget().getClass());
String methodName = joinPoint.getSignature().getName();
String typeName = joinPoint.getSignature().getDeclaringTypeName();
logger.info("Executando metodo: " + methodName + " da classe: " + typeName);
}
@Pointcut("execution(* br.com.caelum.fj27.gerenciador..*.*(..))")
public void metodosDeGerenciadores() {}
}
O parmetro que representa o retorno pode ser de qualquer tipo. Porm, o Spring AOP s aplicar o aspecto
nos mtodos que tiverem retorno compatvel, mesmo que o mtodo esteja includo no pointcut associado. Para
fazer com que o advice seja aplicado a qualquer join point, usamos object como tipo de retorno aceito.
II
Captulo 8 - Apndice - AOP - Programao Orientada a Aspectos - Para saber mais: Combinando Pointcuts - Pgina 83
Caelum http://www.caelum.com.br
Spring Framework 3
@Pointcut("execution(public * *(..))")
public void metodosPublicos() {}
@Pointcut("execution(void set*(*))")
public void comecamComSetERecebemSoUmParametro() {}
@Pointcut("metodosPublicos() && comecamComSetERecebemSoUmParametro()")
public void setters() {}
Podemos ainda definir o conjunto de join points diretamente na definio do advice, sem precisar criar o
pointcut explicitamente:
@Before("execution(public Session br.com.caelum.fj27..*.getSession(..))")
public void fazAlgoAntesDeMetodosQueCriamSession() {
//
}
@Around
@Around
public Object controleDeAcesso(ProceedingJoinPoint joinPoint) {
if(podeExecutar()) {
return joinPoint.proceed();
} else {
throw new IllegalAccessException();
}
Captulo 8 - Apndice - AOP - Programao Orientada a Aspectos - Para saber mais: @Around - Pgina 84
CAPTULO
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:integration="http://www.springframework.org/schema/integration"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xscr>
A novidade aqui o namespace integration junto com a declarao do schema. Registrando isso podemos habilitar as anotaes do Spring Integration. Alm disso, necessrio avisar que qualquer canal de
comunicao pode ser criado automaticamente:
<integration:annotation-driven />
<integration:message-bus auto-create-channels="true"/>
O prximo passo criar uma classe que funcione como um filtro da comunicao. Um Handler recebe
uma mensagem de entrada e devolve uma mensagem para a saida. Entretanto o handler pode transformar a
mensagem, rotear a outro lugar, executar qualquer regra de negcio ou parar o envio da mensagem.
Um Handler usa a anotao @MessageEndpoint informando qual a entrada e saida. Por exemplo:
OMessageEndpoint(input="inputChannel", output = "outputChannel")
public class MessageHandler {
//...
}
Aqui a entrada representada pelo inputChannel e a saida pelo outputChannel. Se os canais de comunicao no existem ainda, o Spring Integration faz a criao automaticamente (auto create channels="true").
-
O mtodo que recebe a mensagem deve usar a anotao @Handler. A mensagem pode ser qualquer objeto
e o nome do mtodo pode ser escolhido livremente. Por exemplo:
85
Spring Framework 3
Caelum http://www.caelum.com.br
Neste exemplo fica clara a simplicidade, estamos recebendo um projeto (a mensagem de ida) e devolvendo
um projeto (a mensagem de retorno).
Falta criar uma classe que fica escutando a saida e recebendo, finalmente, a mensagem. Esta classe um
bean comum do Spring que tem um mtodo para receber a mensagem. preciso usar a antotao @Subscriber.
Veja o exemplo:
@Component
public class MovimentacaoListener {
0Subscriber(channel="outputChannel")
public void onMessage(Movimentacao movimentacao) {
System.out.println("Chegou movimentacao: " + movimentacao);
//mais cdigo aqui
}
O bean define com a anotao @Subscriber(channel="outputChannel) qual o canal para escutar. Isto
parecido com um MessageListener da especificao JMS. O nome do mtodo e o tipo do parmetro podem
ser escolhidos livremente desde que o parmetro seja compativel com o retorno do handler (Movimentacao
handle(Movimentacao movimentacao)).
O ltimo passo criar e enviar a mensagem. Para o envio preciso usar um objeto Spring do tipo
que d acesso a qualquer canal de comunicao. Podemos injetar o bean:
ChannelRegistry,
@Autowired
private ChannelRegistry channelRegistry;
A classe
GenericMessage
caelum/27/integration/
Caelum - http://www.caelum.com.br
Spring Framework 3
3) Crie a classe MessageHandler que representa o roteador no pacote br com. caelum. estoque . int egration:
@Component
public class MovimentacaoListener {
@Subscriber(channel="outputChannel")
public void onMessage(Movimentacao movimentacao) {
System.out.println("Chegou movimentacao: " + movimentacao);
}
@Autowired
private ChannelRegistry channelRegistry;
6) Altere o mtodo entrada(). Crie e envie uma mensagem:
//...
ri Caelum http://www.caelum.com.br
Spring Framework 3
CAPTULO
10
value="smtp.servidorDeEmail.com" />
1>
value="465" />
</bean>
Assim possvel injetar o bean num componente do Spring, por exemplo atravs da anotao @Autowired:
@Service
public class GeraMovimentacao {
private MailSender mailSender;
Aqui tambm usamos a interface org.springframework.mail .MailMessage em vez da implementao declarado no XML.
89
Spring Framework 3
Caelum http://www.caelum.com.br
1) Adicione o mail.jar no buildpath do seu projeto. O jar se encontra na pasta lib/j2ee da distribuio do
Spring.
2) Defina o bean para o envio de mensagens. Preencha as propriedades do host, username e password com os
valores do seu provedor.
<bean id"mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"
<property name="host"
value="smtp.servidorDeEmail.com" />
value="465" />
<property name="javaMailProperties"
<props>
<prop key="mail.smtps.auth">true</prop>
<prop key="mail.smtps.starttls.enable">true</prop>
<prop key="mail.smtps.debug">true</prop>
</props>
</property>
</bean>
4) Injete os beans mailMessage e mailSender na classe GerenciadorTarefa. Adicione o cdigo para enviar o
email no mtodo novo.
Caelum http://www.caelum.com.br
Spring Framework 3
FJ-25:
Java e Orientao a
objetos
FJ-16:
FJ-26:
FJ-19:
FJ-31:
Java EE avanado e
Web Services
FJ-21:
FJ-91:
Arquitetura e Design de
Projetos Java
RR-71:
RR-75: