Professional Documents
Culture Documents
Injection
By Felipe Saab
Quem dessa rea e nunca ouviu falar de Spring Framework por favor levante a
mo.. timo, todo mundo pelo menos j ouviu falar desse tal de Spring.
Durante esta srie de tutoriais eu vou demonstrar o que , pra que server e como vc pode
utilizar o Spring para facilitar seus projetos.
Primeiramente, o que esse tal de Spring afinal de contas? Spring um framework que
propicia um container para sua aplicao, ou seja, sua aplicao roda em cima do
container do Spring. Tal container prov principalmente a Injeo de Dependncia e a
Programao Orientada a Aspectos.
Nesta primeira parte eu vou falar sobre o padro Injeo de Dependncia e os benefcios
que ele prov. Vamos l ento:
O padro de projeto Injeo de Dependncia diz que uma classe no deve criar as suas
dependncias, em outras palavras, uma classe no deve instanciar seus atributos. Ao
invs disso, eles devem ser injetados na classe atravs de um mecanismo externo, ou
atravs de quem criou o objeto desta classe.
Ainda no est claro n Vamos a um exemplo (baseado em um dos exemplos do livro
Spring in Action 2, 2008) para facilitar as coisas.
Vamos imaginar que fomos contratados para desenvolver o software do dolos Spring?,
uma competio de talentos musicais onde as pessoas cantam e/ou tocam. Primeiro de
tudo vamos desenvolver uma interface genrica o suficiente para todos os nossos
competidores:
Pronto, agora todo mundo que for competir no dolos Spring vai ter que implementar essa
interface para poder participar ( quase como se fosse a inscrio da competio
hahahahah pssima analogia.. ).
Vamos ento aos competidores, vamos comear com um cantor. Um cantor no toca
nenhum instrumento musical, ele apenas canta uma msica. Vamos ento interface que
descreve uma msica:
Legal, algum vai cantar a famosa cantiga Atirei o pau no gato. Vamos codificar o cantor
que ir fazer isso ento:
public Cantor() {
this.musica = new AtireiOPauNoGato();
}
Prontinho. Agora toda vez que um cantor for se apresentar ele ir cantar Atirei o Pau no
Gato. Mas pera ai, tem alguma coisa que no faz muito sentido aqui no tem?
Ser que TODO cantor da nossa competio vai cantar a mesma msica? Acho que no
n E do jeito que esta classe est implementada, ns temos que mudar a classe para
ele cantar uma outra musica. Isso no l muito vivel
O que a Injeo de Dependncia prega que ao invs de o cantor se preocupar em ficar
preparando a msica que ele vai cantar, ele apenas vai receber a msica impressa um
pouco antes da competio e pronto.
Pensando nisso, o cantor ficaria assim:
public Cantor() {
}
</beans>
</beans>
Aqui que o Spring comea a trabalhar, cada tag no arquivo de configurao representa
um objeto que o container ir gerenciar. No nosso exemplo, temos o bean atireiopau que
um objeto da classe AtireiOPauNoGato e o bean jose que eh um Cantor e vai cantar a
msica referenciada pelo bean atireiopau.
Em termos de cdigo, seria como se o container fizesse o seguinte:
competidor.apresentar();
}
public CantorTocador() {
super();
}
Pronto, agora nosso prximo competidor est pronto para se apresentar. Sras e Srs dem
as boas vindas para Carlos:
</beans>
competidor.apresentar();
}
}
E se a gente quisesse que o Carlos tocasse piano cantando Atirei o Pau no Gato?
Simples:
</beans>
Basta injetar os beans desejados nas dependncias e boa! Tudo segue seu fluxo natural.
Esta foi uma pequena (eu diria mnima) introduo Injeo de Dependncias no Spring
Framework. O container ApplicationContext possui muuuuuito mais funes para gerenciar
os seus beans, porm isso iria extender demais este assunto.
Acho que se voc leu o post at aqui, vc conseguiu pegar o que eu quis passar: programar
utilizando interfaces torna o seu cdigo muito mais limpo, fcil de entender, fcil de dar
manuteno, fcil de reutilizar, fcil de extender, ou seja, melhora tudo!!
Adicionando isso com a tcnica de Injeo de Dependncia torna a aplicao altamente
desacoplada em termos de componentes (voc pode utilizar a classe Cantor em qualquer
outra aplicao, s precisa passar uma classe que implemente a interface Musica pra ele e
tudo vai funcionar perfeitamente).
D uma boa olhada no cdigo O que tem a ver mesmo com o checkout so apenas as
linhas:
carrinho.getCliente().debitar(carrinho.getValorTotal());
carrinho.finalizarCompra();
public Plateia() { }
public Cantor() {
}
OBS: A platia estaria sendo injetada na classe Cantor pelo container assim como est
acontecendo com a musica.
Sem a orientao a aspectos, toda classe que implemente a interface Competidor tem que
ficar se preocupando com a platia. No certo o competidor ficar mostrando os assentos
para a platia, pedir para elas desligarem os celulares, se apresentar e por fim pedir para
eles aplaudirem ou vaiarem. Isso coisa que a platia tem que fazer sozinha!
Vamos utilizar todo o poder da orientao a aspectos para que a platia consiga fazer isso
sozinha. A classe Cantor volta a ser o que era antes, se preocupando somente em cantar a
sua musica:
public Cantor() {
}
@Aspect
public class Plateia {
public Plateia() { }
@Pointcut("execution(* *.apresentar(..))")
public void apresentacao() {}
@Before("apresentacao()")
public void sentar(){
System.out.println("A platia est sentando");
}
@Before("apresentacao()")
public void desligarCelulares(){
System.out.println("A platia est desligando os celulares");
}
@AfterReturning("apresentacao()")
public void aplaudir(){
System.out.println("CLAP CLAP CLAP UHULL CLAP CLAP");
}
@AfterThrowing("apresentacao()")
public void vaiar(){
System.out.println("UUUUUU!! E FORA! E FORA! UUUUU!! ");
}
}
-> Depois que o pointcut est definido, basta anotar os mtodos desejados especificando
quando e onde eles devem ser executados. Para especificar onde os mtodos sero
executados utilizado o nome do mtodo que est anotado com o pointcut (no nosso
exemplo a string apresentacao()) e para especificar quando existem algumas outras
anotaes. Creio que as mais utilizadas
so: @Before, @AfterReturning e @AfterThrowing(caso queira saber quais so as outras,
fique a vontade para fuar na documentao).
E voil, nosso aspecto est pronto! Para ele funcionar s falta mais uma coisinha, que no
to pequena assim (pelo menos na teoria, na prtica sim! ).
Para a orientao a aspectos funcionar devem ser criadas cpias dos objetos que
queremos interceptar. Tais cpias contm os advices (cdigos das cross-cut concerns) e
esse objeto que recebe as requisies, executa os advices e depois passa a requisio
para o mtodo do objeto original. Essas cpias de objetos que contm os advices so
chamados de Proxy (no plural: Proxies).
E at algumas verses atrs os proxies tinham que ser criados na mo!!! Imagine que
trabalho que dava Porm o Spring prov um mecanismo automtico para a criao dos
proxies (ufa!).
Segue o arquivo de configurao para podermos fazer nosso exemplo funcionar:
Vamos ao nosso prximo assunto ento: JDBC e como o Spring facilita esse cara pra
gente!
Existem apenas 2 conceitos bsicos que o Spring nos sugere para o acesso rpido e fcil
aos dados atravs de drivers JDBC: DataSource (DAO fornecido pelo Spring) e
JdbcTemplate (template para acesso a dados atravs de um driver JDBC fornecido pelo
Spring):
DataSource uma classe provida pelo nosso querido Spring Framework e que se
preocupa com toda a conexo e conversao com o driver JDBC que iremos utilizar (neste
exemplo iremos utilizar o driver JDBC para o banco de dados MySQL). Em palavras um
pouco mais tcnicas, esta classe ir se resonsabilizar por nos entregar um
objetojava.sql.Connection, java.sql.Statement, entre esses objetos que utilizamos
quando o assunto conexo de dados JDBC.
JdbcTemplate uma outra classe muito til provida pelo nosso adorado Spring! Este
template prov mtodos padronizados para acessarmos os dados (selecion-los e
manipul-los), ou seja, fazer tudo o que podemos fazer com eles de um jeito bem fcil de
acessar (como voc ir descobrir logo a frente).
Muito bem, com esses dois conceitos j pintados na sua cabea, por favor responda
corretamente a prxima pergunta: J que estas classes so providas pelo nosso amigo
Spring, quem instancia elas?. Exato!! O prprio Spring cria os beans para estas classes,
tudo o que temos que fazer declarar estes beans no arquivo de configurao do Spring.
Vamos a ele ento:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/spring_jdbc" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
To simples quanto encher um copo com gua! Eu sinto at orgulho de mostrar como
fcil conectar com uma fonte de dados JDBC atravs do Spring!
O nosso primeiro bean o dataSource. Como eu j disse ali em cima, esse o cara
responsvel por conversar com o driver JDBC. Ento nada mais justo que a gente ensinar
ele a se comunicar com o driver passando os parmetros corretos no Os parmetros
(injetados atravs da DI) passados para o dataSource foram: dataClassName (qual driver
JDBC ser usado), url (caminho do JDBC para se conectar at o banco de dados
desejado), username (login do banco de dados) e password (senha do banco de dados).
No caso desse nosso exemplo, o driver JDBC do MySQL, nunca esquea que o driver
deve estar no classpath em!!! Segue uma foto te ensinando como adicionar o driver JDBC
do MySQL no NetBeans:
E o segundo bean do arquivo de configurao o jdbcTemplate que o template provido
pelo Spring para facilitar a manipulao dos dados. A unica dependncia que este bean
tem o prprio dataSource, ou seja, ele precisa de uma referncia para um dataSource
para saber em qual banco ele vai executar os comandos que quisermos. Sendo assim, de
um jeito muito fcil (injeo de dependncia), ns passamos uma referncia do
nossodataSource j criado para ele. ^^
S fazendo uma pequena pausa no Spring agora, vamos falar um pouquinho do banco de
dados: criei uma tabela de Pessoas para fazermos nossos teste aqui. D uma olhada no
script de criao:
Muuuito bem! A esta altura ns j temos os arquivos base para a conexo com o banco de
dados E o banco de dados! Vamos continuar a codificar mais algumas classes e veremos
o quo fcil o Spring tornou o acesso aos dados atravs do JDBC para ns.
J que temos a tabela pronta no banco de dados, precisamos desenvolver uma classe
Java que ir representar cada registro dessa tabela (para manipulao em memria n):
Pessoa.java
@Override
public String toString() {
return "Codigo: " + String.valueOf(this.cod)
+ " | Nome: " + this.nome
+ " | Idade: " + String.valueOf(this.idade);
}
}
Pronto, com a classe Pessoa agora fica bem mais fcil ns termos uma Pessoa em
memria! Vamos daqui pra frente tentar unir a classe Pessoa que acabamos de escrever
com o banco de dados, ou seja, inserir um objeto da classe Pessoa no banco de dados,
recuperar um objeto Pessoa do banco, e por ai vai
Caso voc acompanha este blog regularmente, j deve ter percebido que eu virei f de
utilizar as interfaces no desenvolvimento (os motivos so muitos! ento apenas acredite:
compensa!). J que eu sou um cara que gosta de seguir padres e tudo mais, vamos
codificar uma interface que ser muito til para o nosso acesso ao banco de dados:
IBanco.java
public BancoDados() {
}
return pes;
}
}
Muito bem, vamos com calma
A primeira coisa que devemos notar que temos uma varivel de instncia
chamadajdbcTemplate. Como eu imagino que voc leitor um cara que associa tudo
muito rpido e j viu que um pouco mais pra baixo dessa varivel tem o setter dela, eu
imagino que voc j entendeu que essa varivel vai ser injetada atravs do container do
Spring n =]
Como ns j pedimos pro Spring criar um bean do tipo JdbcTemplate no arquivo de
configurao, daqui a pouco ns voltaremos nele e pediremos pra ele enviar uma
referncia do JdbcTemplate aqui pra essa nossa classe poder usar. Tudo muito prtico!
Depois disso o que ns podemos entender que a classe reescreveu os mtodos que a
interface obriga e os implementou de fato! Vamos entender eles rapidinho
Para as operaes de insero, alterao e excluso os mtodos so absolutamente
iguais: primeiro foi definido o SQL referente a operao e depois foi chamado o
mtodoupdate da nossa classe super amigvel: jdbcTemplate.
Existem dois detalhes para chamar a ateno na execuo destas operaes:
1 O SQL que foi montado no possui os valores finais. Ao invs deles esto presentes
pontos de interrogao ( ? ) representando parmetros.
2 Os parmetros informados no SQL so enviados como o segundo parmetro do
mtodo update do jdbcTemplate. Para a padronizao na chamada do mtodo
necessrio que todos os parmetros definidos no SQL sejam passados dentro de um vetor
de Object (mesmo que seja s um parmetro, ele tambm deve estar dentro do vetor).
Outra coisa que vale ser lembrada que o primeiro item do vetor ir ser includo no lugar
do primeiro parmetro do SQL (primeiro ponto de interrogao) ento tome cuidado para
passar os parmetros na ordem correta para o vetor de Object.
S mais um comentrio para a operao de seleo: tambm foi utilizado um SQL com
parmetro e um vetor de Object referenciando os parmetros porm o mtodo
dojdbcTemplate utilizado no foi o update, e sim o query!
O funcionamento deste mtodo bem interessante: ao invs de apenas 2 parmetros
(como no mtodo update), depois do vetor de Object passado um
objetoorg.springframework.jdbc.core.RowMapper que responsvel por mapear cada
registro retornado por um ResultSet interno ao jdbcTemplate em um objeto
correspondente ao registro e aps o mapeamento o objeto customizado adicionado a
umjava.util.List que o retorno final do mtodo query.
Exemplo do nosso caso: caso sejam retornadas 3 pessoas pelo SQL utilizado, o
mtodomapRow do objeto RowMapper ser invocado 3 vezes (uma para cada registro).
Cada vez que ele for invocado ser configurado um novo objeto Pessoa com os dados de
cada registro retornado do banco de dados e cada vez que o mtodo retornar um novo
objeto Pessoa ele ser guardado na lista que ser retornada pelo mtodo query.
Logo depois deste mtodo eu apenas retorno o primeiro objeto que est na lista ou ento
retorno um objeto com o mesmo cdigo da busca caso no foi encontrado nenhum
registro.
Sempre muito simples esses cdigos adoro isso ehheheh
Vamos adicionar um bean ao arquivo de configurao do Spring para o container tambm
instanciar a nossa classe responsvel pelo banco de dados e claro, vamos tambm j
injetar o JdbcTemplate no nosso novo bean:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/spring_jdbc" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
E isso tudo!!
Nossas classes base para o acesso e manipulao da entidade Pessoa no banco de
dados e no nosso prprio software j esto codificadas e bem funcionais. Tudo que
precisamos agora de um programinha exemplo para testarmos estas classes e ver como
iriam ser realmente usadas.
Segue a classe que contm o mtodo main desta aplicao:
//insere no banco
try {
banco.InserirPessoa(p1);
System.out.println("1 - Pessoa Inserida");
} catch (Exception e) {
System.out.println("No foi possvel inserir o registro: " + e);
}
//recupera
try {
felipe = banco.SelecionarPessoa(p1);
System.out.println("2 - Pessoa recuperada: " + felipe);
} catch (Exception e) {
System.out.println("No foi possvel recuperar o registro: " + e);
}
//altera
try {
banco.AtualizarPessoa(p2);
System.out.println("3 - Pessoa Atualizada");
} catch (Exception e) {
System.out.println("No foi possvel atualizar o registro: " + e);
}
//recupera novamente
try {
felipe = banco.SelecionarPessoa(p2);
System.out.println("4 - Pessoa recuperada: " + felipe);
} catch (Exception e) {
System.out.println("No foi possvel recuperar o registro: " + e);
}
//apaga
try {
banco.ApagarPessoa(p2);
System.out.println("5 - Pessoa Apagada");
} catch (Exception e) {
System.out.println("No foi possvel apagar o registro: " + e);
}
Assumindo ento que agora voc j sabe sobre o Spring, vamos ver como ele facilita o
uso do framework de mapeamento objeto-relacional Hibernate (verso 3.x).
OBS: Irei comentar detalhadamente apenas coisas relacionadas ao Spring, ou seja,
espero que voc j possua o conhecimento necessrio do Hibernate.
Apenas para lembrarmos como o Spring tenta padronizar o acesso a dados em uma base
de dados vamos dar uma olhada na imagem do post anterior (retirada do livro Spring in
Action 2, Manning 2008):
S com o pargrafo acima j deu pra ver que precisaremos criar e configurar alguns beans
no Spring: um data source que se comunique com a base de dados e um template que se
comunique com o Hibernate.
Vamos a eles ento:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/hibernate_teste" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Como voc pode notar o template precisa de uma referncia para um bean que seja uma
implementao de org.hibernate.SessionFactory e a depende de como voc est
acostumado a mapear os objetos para tabelas: utilizando XML ou Anotaes.
Utilizando XML:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>Livro.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop
key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryB
ean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>spring_hibernate.modelo.Livro</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop
key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
Os beans so bem parecidos, precisam de pelo menos trs propriedades setadas via
injeo de dependncia:
dataSource -> referncia para o data source
hibernateProperties -> uma lista (do tipo props, onde tanto a chave (key) quanto o valor
so Strings) contendo as propriedades da session factory que queremos setar
E a terceira propriedade depende do bean que est sendo declarado:
se for LocalSessionFactoryBean utiliza-se a propriedade mappingResources que
recebe uma lista contendo todos os XMLs responsveis pelo mapeamento
se for AnnotationSessionFactoryBean utiliza-se a propriedade annotatedClassesque
recebe uma lista contendo todas as classes que representam o mapeamento
OK. Estes so os beans que o Spring fornece para facilitar a nossa vida ao utilizar o
Hibernate. Vamos ento a um pouco de cdigo para demonstrar como ns utilizamos tais
beans nas nossas prprias classes.
Primeiro de tudo, no banco de dados vamos criar uma tabela para os testes:
Nossa tabela ento ser sobre livros, contendo apenas o cdigo e o ttulo do livro, vamos a
classe que vai representar um livro:
public Livro() {
}
Como a tabela contm apenas dois campos o mapeamento fica bem simples: a chave
primria que a varivel de instncia cod (coluna liv_cod) e a varivel de instncia titulo
ligada a coluna liv_titulo.
S chamando a ateno para o generator do cod, que increment, ou seja, no
precisamos setar o cdigo de um novo livro para adicion-lo ao banco, o novo cdigo ser
gerado sozinho pelo Hibernate.
Muito bem, agora que o mapeamento da classe Livro para a tabela Livros est feito
vamos comear a escrever o nosso DAO. Primeiro vamos a interface (gosto de reforar a
idia de que interfaces devem ser utilizadas):
Acho que o nico comentrio que essa interface merece no mtodo InserirLivro porque
ele recebe o Livro que ser inserido e retorna outro Livro. Isso se deve ao fato da varivel
de instncia cod do Livro ser nula quando o livro estiver sendo gravado no banco e aps a
sua gravao o Hibernate retorna qual valor foi atribudo como cdigo, ou seja, depois de
gravado no banco que ns descobrimos o valor do cdigo, ento apenas atualizamos o
objeto Livro com o seu novo cdigo e retornamos um objeto consistente.
Vamos implementao dessa interface para podermos ver como o template do Hibernate
facilita a nossa vida:
HibernateTemplate hibernateTemplate;
//utilizando HQL
//String hql = "from Livro where titulo like '%"+titulo+"%'";
//return hibernateTemplate.find(hql);
}
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/hibernate_teste" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>Livro.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop
key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
Como exemplo final eu s gostaria de deixar um cdigo de como utilizar essa classe
BancoDados:
livro1 = banco.InserirLivro(livro1);
livro2 = banco.InserirLivro(livro2);
System.out.println("Livros gravados no banco");
livro2 = banco.SelecionarLivroPorCodigo(2);
exibe(livro2);
System.out.println("Atualizando livro");
livro1.setTitulo(livro1.getTitulo()+" Segunda Edio");
banco.AtualizarLivro(livro1);
System.out.println("Apagando livro");
banco.ApagarLivro(livro2);
}
Muito bem leitor, se voc chegou at aqui voc vai desenvolver com uma produtividade
bem alta porque agora que voc j sabe Hibernate E Spring voc pode se concentrar
muito mais na camada de negcios, ou seja, no precisa perder muito tempo codificando
acesso a banco de dados e operaes necessrias para manipulao dos dados.
Antes de entrar no assunto deixe-me dar aquele aviso de praxe: caso voc no ainda no
conhea o bsico do Spring Framework d uma olhadinha nos posts anteriores dessa
srie:
Aps essa pequena introduo terica vamos ver como o Spring implementa esse padro:
Figura 1 Fluxo
de informaes no Spring Web MVC
1 A requisio do usurio sempre chega em um nico servlet, o Dispatcher Servlet. Esse
um padro muito comum em frameworks MVC chamado front controller (controlador
frontal) onde um nico servlet responsvel por receber a requisio, delegar o
processamento para os outros componentes da aplicao e devolver uma resposta para o
usurio;
2 Uma vez que o Dispatcher Servlet tem a requisio ele precisa descobrir para
qualcontroller essa requisio ser enviada. Para isso ele pede ajuda para o Handler
Mapping. Baseado na URL da requisio ele indica qual o controlador a ser invocado;
3 A requisio ento enviada para o controlador que cuidar dos dados contidos nela.
Uma vez que o controlador recebe a requisio ele processa os dados que ela trouxe e
executa alguma regra do negcio da aplicao (faz uma venda, adiciona um novo produto
no estoque, aluga um dvd, abre uma os, );
4 Frequentemente a lgica processada pelo controlador resulta em alguma informao
que deve ser levada de volta para o usurio (o model). Somente enviar a informao de
volta no o suficiente, ela deve ser formatada de um jeito que o usurio a entenda, para
isso ela deve ser enviada para a view. Para isso o model e alguns dados da view so
encapsulados em um objeto ModelAndView e retornados ao Dispatcher;
5 Como o controlador no fica preso a apenas uma view ele envia uma dica no
objetoModelAndView para o Dispatcher saber para qual view deve enviar os dados. O
Dispatcher repassa essa dica para o ViewResolver que devolve qual view deve ser
chamada;
6 Por fim o Dispatcher envia as informaes (model) para a view (pgina JSP) que
acabou de descobrir. A pgina renderiza as informaes recebidas e devolvida ao
usurio.
Simples de entender, no!?
Vamos por a mo na massa ento! A IDE ser o NetBeans 6.9.1. Crie uma nova Aplicao
Web (Java Web > Aplicao Web) e no ltimo passo do assistente selecione o framework
Spring Web MVC (Figura 2) e finalize.
<bean name="indexController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController"
p:viewName="index" />
O controlador que ser responsvel por uma requisio pgina index.jsp. Como esse
controlador no vai fazer nenhum processamento dos dados da requisio foi criado um
controlador da classe ParameterizableViewController que o Spring Web MVC j tem
implementado. O que ele faz apenas repassar um ModelAndView informando o nome da
view (informado no parmetro viewName) para o Dispatcher.
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="index.htm">indexController</prop>
</props>
</property>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
O viewResolver quem vai ajudar o Dispatcher a saber para qual view encaminhar
oModelAndView que o controlador retornar (passo 5 do fluxo ali em cima). Como
noModelAndView s vai ter o nome lgico da view (no caso da index vai ter apenas a
Stringindex que foi definida no controlador indexController), o que o viewResolver faz
criar o caminho da view utilizando o prefixo e sufixo informados na declarao do bean.
Para descobrir o caminho da view index ele vai fazer:
WEB-INF/jsp/ + index + .jsp => resultando em WEB-INF/jsp/index.jsp
E por fim o NetBeans criou um bean sem id:
<bean
class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerM
apping"/>
Esse bean o cara que vai analisar todas as outras URLs e mape-las para os nossos
controladores. Isso vai ficar mais claro daqui a pouquinho quando fizermos o nosso
controlador.
Bom, vamos a nossa aplicao ento, vou utilizar a idia de uma aplicao que tem no
livroUse a Cabea: Servlets & JSP: uma aplicao que d conselhos sobre cerveja! O
usurio diz o tipo da cerveja que ele quer e nossa aplicao retorna alguns conselhos
sobre quais cervejas ele poderia tomar.
Vamos aproveitar a estrutura que o NetBeans j criou e apenas alterar a pgina index.jsp
para ser o nosso formulrio onde o usurio vai indicar qual tipo de cerveja ele quer tomar:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Cervejeiro Online</title>
</head>
<body>
<h3>Eu sou um cervejeiro profissional!</h3>
<form action="sugestor.htm" method="post">
<p>
Qual tipo de cerveja voc prefere?
<select name="tipo">
<option value="pilsen">Pilsen</option>
<option value="chop">Chop</option>
<option value="malzbier">Malzbier</option>
</select>
</p>
<p><input type="submit" value="Quer saber minha sugesto?" /></p>
</form>
</body>
</html>
Quando o usurio pedir o conselho da nossa aplicao sobre cervejas do tipo que ele
selecionou os dados sero enviados via POST para a URL sugestor.htm.
Lembra daquele bean que o NetBeans criou sem id
(ControllerClassNameHandlerMapping)? Ento, ele vai entrar em ao agora! Ele vai ser
chamado pelo Dispatcher para identificar qual controlador vai receber a requisio.
O processo que esse bean utiliza para identificar qual controlador vai ser chamado
apenas pegar a String existente na URL at .htm e concatenar com Controller. Nesse
caso o controlador que vai ser chamado o SugestorController. Caso nosso formulrio
enviasse os dados para cervejeiro.htm o controlador que o Dispatcher tentaria invocar
seria o CervejeiroController. Facinho no? ^^
Sendo assim vamos construir o nosso controlador:
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
return modelAndView;
}
}
Uma coisa que no podemos esquecer: para o Spring entender que essa nossa classe
um controlador ela deve herdar de AbstractController ou qualquer outra classe que
represente um controlador para o Spring Web MVC (pode ver mais exemplos aqui
na referncia oficial).
Uma boa prtica no desenvolvimento de aplicativos MVC nunca deixar toda a lgica
dentro do controlador, usar ele apenas para delegar funes para outros componentes
para que esses sim executem a lgica.
Para isso servem os famosos Services! Nosso controlador recebe um SugestorService
(injetado pelo Spring) e delega para ele a funo de sugerir as cervejas para o usurio.
Depois que o SugestorService retorna uma lista de cervejas (j j veremos a
classe Cervejaque faz parte do model da nossa aplicao) para o controlador hora de
montar oModelAndView que deve ser retornado para o Dispatcher.
Nosso ModelAndView foi instanciado com 3 parmetros sendo o primeiro o nome da view
(aquela dica que o bean viewResolver vai usar para montar o nome do arquivo lembra?),
o segundo o nome que a lista de cervejas ter na pgina JSP e o terceiro a lista
propriamente dita, quer dizer, quando formos mostrar as cervejas na pgina WEB-
INF/jsp/sugestao.jsp (eu sei que voc lembra como o viewResolver acha o arquivo da
view, s escrevi ele ali pra voc no esquecer mesmo.. hehehe) a lista ter o
nomecervejas.
if (tipo.equals("pilsen")){
cervejasSugeridas.add(criarCerveja("Skol", 600));
cervejasSugeridas.add(criarCerveja("Antartica", 600));
} else if (tipo.equals("chop")){
cervejasSugeridas.add(criarCerveja("Brahma", 600));
} else if (tipo.equals("malzbier")){
cervejasSugeridas.add(criarCerveja("Brahma Malzbier", 355));
cervejasSugeridas.add(criarCerveja("Caracu", 355));
}
return cervejasSugeridas;
}
Apenas verificamos qual o tipo que o usurio selecionou e montamos uma lista de cervejas
a partir dele (desculpe se no gosta das que coloquei, juro que tentei ser bem simples e
ecltico).
A classe que representa nosso modelo um simples POJO com o nome e a quantidade
(em mls) da cerveja:
Bom, com toda a regra de negcio implementada vamos para a pgina que vai mostrar as
cervejas sugeridas pela nossa aplicao (sugestao.jsp):
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Minha Sugesto</title>
</head>
<body>
<h3>Eu acho que voc deveria experimentar:</h3>
<p>
<c:forEach items="${cervejas}" var="cerveja">
<li>
<c:out value="${cerveja.nome}"/> -
<c:out value="${cerveja.quantidade}"/>ml
</li>
</c:forEach>
</p>
<p>
<a href="index.htm">Voltar</a>
</p>
</body>
</html>
<bean id="sugestorService"
class="br.javasimples.cervejeiro.service.impl.SugestorServiceImpl" />
<bean name="sugestorController"
class="br.javasimples.cervejeiro.controller.SugestorController">
<property name="sugestor" ref="sugestorService" />
</bean>
NOSSA APLICAO
Vamos desenvolver um pequeno programa para simular o funcionamento de um
computador ento.
O projeto vai ter a seguinte estrutura:
OBS: a pasta lib contm todas as bibliotecas necessrias para rodar o projeto.
Vamos comear dando uma olhada no pacote br.blog.javasimples.spring.pecas para
vermos as interfaces que definem as pecas do computador:
/**
* Interface que define os mtodos necessrios para uma pea do computador.
*
* @author Felipe
*/
public interface Peca {
void ligar();
}
/**
* Interface que define os mtodos necessrios para um Monitor.
*
* @author Felipe
*/
public interface Monitor extends Peca {
/**
* Escreve uma mensagem na tela.
*/
void escreverNaTela(String mensagem);
}
/**
* Interface que define os mtodos necessrios para um Processador.
*
* @author Felipe
*/
public interface Processador extends Peca {
/**
* Soma dois inteiros.
*/
int soma(int x, int y);
/**
* Classe que representa um monitor CRT de 17 polegadas.
*
* @author Felipe
*/
public class Monitor17 implements Monitor {
/**
* {@inheritDoc}
*/
@Override
public void ligar() {
System.out.println("Iniciando o monitor [" + FREQUENCIA +
"Hz];");
}
/**
* {@inheritDoc}
*/
@Override
public void escreverNaTela(String mensagem) {
System.out.println("[" + mensagem + "]");
}
}
/**
* Classe que representa um processador DualCore.
*
* @author Felipe
*/
public class DualCore implements Processador {
/**
* {@inheritDoc}
*/
@Override
public void ligar() {
System.out.println("Iniciando o processador [" + FRQUENCIA +
"GHz, " + NUCLEOS + " ncleos];");
}
/**
* {@inheritDoc}
*/
@Override
public int soma(int x, int y) {
return x + y;
}
}
/**
* Classe que representa o computador.
*
* @author Felipe
*/
public class Computador {
<bean id="processador"
class="br.blog.javasimples.spring.pecas.impl.DualCore"/>
<bean id="monitor"
class="br.blog.javasimples.spring.pecas.impl.Monitor17"/>
</beans>
Ligando computador...
Iniciando o processador [2.6GHz, 2 ncleos];
Iniciando o monitor [60Hz];
Computador ligado.
[2 + 2 = 4]
Preparando o container
Por padro o container do Spring no vem com o suporte para injeo de dependncia via
anotaes habilitado. Para fazer isso ns precisamos fazer uma pequena alterao no
arquivo de configurao:
<context:annotation-config />
<bean id="processador"
class="br.blog.javasimples.spring.pecas.impl.DualCore"/>
<bean id="monitor"
class="br.blog.javasimples.spring.pecas.impl.Monitor17"/>
</beans>
Hoje vamos ver somente a @Autowired. As outras so bem parecidas e ficam por sua
conta o estudo ok?
A anotao @Autowired
A anotao @Autowired pode ser utilizada em setters, construtores ou at mesmo em uma
varivel de classe independe da sua visibilidade, ou seja, mesmo que private.
Na nossa aplicao podemos utiliz-la assim:
// ...
@Autowired
private Processador processador;
@Autowired
private Monitor monitor;
// ...
@Autowired
public void setProcessador(Processador processador) {
this.processador = processador;
}
@Autowired
public void setMonitor(Monitor monitor) {
this.monitor = monitor;
}
// ...
@Autowired
private Processador processador;
@Autowired(required=false)
private Monitor monitor;
// ...
Porm quando fizer isso tome o cuidado de sempre verificar se o atributo est nulo ou no
antes de utiliz-lo para evitar os famosos NullPointerException.
Outra coisa que devemos saber que o Spring realiza uma busca por tipo (byType) para
encontrar as dependncias para atributos anotados com @Autowired, ou seja, como
anotamos um atributo do tipo br.blog.javasimples.spring.pecas.Monitor o Spring vai
procurar por algum bean que implemente essa interface para poder injetar no nosso
computador.
Como disse anteriormente, caso ele no encontre nenhum bean ser lanada
uma NoSuchBeanDefinitionException.
Mas aqueles momentos em que voc tem uma ideia que voc acha que ningum teve
antes e se ele encontrar mais de um bean que implemente essa interface?
Esse ser o assunto do nosso prximo tpico, e s a nvel de curiosidade, caso isso
acontea e no seja tratado o Spring tambm ir lanar
uma NoSuchBeanDefinitionException.
/**
* Classe que representa um monitor LED de 22 polegadas.
*
* @author Felipe
*/
public class MonitorLED22 implements Monitor {
/**
* {@inheritDoc}
*/
@Override
public void ligar() {
System.out.println("Iniciando o monitor [" + FREQUENCIA +
"Hz];");
}
/**
* {@inheritDoc}
*/
@Override
public void escreverNaTela(String mensagem) {
System.out.println("[[[" + mensagem + "]]]");
}
Nosso novo monitor LED, muito melhor do que o antigo CRT, e tem 22, um tamanho
bem agradvel. Ele to melhor que at o seu mtodo de escrever coloca umas
frescurinhas a mais para tentar agradar o usurio (heheheh). Vamos adicion-lo ao arquivo
de configurao:
<context:annotation-config />
<bean id="processador"
class="br.blog.javasimples.spring.pecas.impl.DualCore"/>
<bean id="monitor"
class="br.blog.javasimples.spring.pecas.impl.Monitor17"/>
<bean id="monitorLED22"
class="br.blog.javasimples.spring.pecas.impl.MonitorLED22"/>
</beans>
@Autowired
private Processador processador;
@Autowired
@Qualifier("monitorLED22")
private Monitor monitor;
// ...
Ao executar nossa aplicao assim ser exibida a seguinte sada indicando que nosso
computador est utilizando o monitor de 22:
Ligando computador...
Iniciando o processador [2.6GHz, 2 ncleos];
Iniciando o monitor [75Hz];
Computador ligado.
[[[2 + 2 = 4]]]
@Autowired
private Processador processador;
@Autowired
@Qualifier("monitor")
private Monitor monitor;
// ...
<bean id="processador"
class="br.blog.javasimples.spring.pecas.impl.DualCore"/>
<bean id="monitor"
class="br.blog.javasimples.spring.pecas.impl.Monitor17">
<qualifier value="monitorCRT17" />
</bean>
<bean id="monitorLED22"
class="br.blog.javasimples.spring.pecas.impl.MonitorLED22"/>
</beans>
@Qualifier("monitorCRT17")
public class Monitor17 implements Monitor {
// ...
@MonitorCRT
public class Monitor17 implements Monitor {
// ...
}
@MonitorLED
public class MonitorLED22 implements Monitor {
// ...
}
Com isso podemos facilmente anotar a dependncia do computador com a nossa nova
anotao:
@Autowired
private Processador processador;
@Autowired
@MonitorLED
private Monitor monitor;
// ...
Nesse momento h aquele flash na sua cabea (de novo) e voc pergunta: e se eu tiver 2
monitores de LED porm com tamanhos diferentes?.
Vamos adicionar um novo monitor de LED ao nosso projeto ento:
/**
* Classe que representa um monitor LED de 19 polegadas.
*
* @author Felipe
*/
@MonitorLED
public class MonitorLED19 implements Monitor {
/**
* {@inheritDoc}
*/
@Override
public void ligar() {
System.out.println("Iniciando o monitor [" + FREQUENCIA +
"Hz];");
}
/**
* {@inheritDoc}
*/
@Override
public void escreverNaTela(String mensagem) {
System.out.println("[[" + mensagem + "]]");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config />
<bean id="monitor"
class="br.blog.javasimples.spring.pecas.impl.Monitor17"/>
<bean id="monitorLED19"
class="br.blog.javasimples.spring.pecas.impl.MonitorLED19"/>
<bean id="monitorLED22"
class="br.blog.javasimples.spring.pecas.impl.MonitorLED22"/>
</beans>
@MonitorLED
@Monitor19
public class MonitorLED19 implements Monitor {
// ...
}
@MonitorLED
@Monitor22
public class MonitorLED22 implements Monitor {
// ...
@Autowired
private Processador processador;
@Autowired
@MonitorLED
@Monitor19
private Monitor monitor;
// ...
}
Ao executar esse cdigo recebemos a seguinte sada indicando que nosso monitor de 19
entrou em ao:
Ligando computador...
Iniciando o processador [2.6GHz, 2 ncleos];
Iniciando o monitor [75Hz];
Computador ligado.
[[2 + 2 = 4]]
Na minha humilde opinio essa a melhor e mais elegante soluo para resolver o
problema. Podemos fazer qualquer combinao com os qualificadores desde que ela
identifique somente um bean.
</beans>
@Component
public class Computador {
// ...
}
@Component
public class DualCore implements Processador {
// ...
}
@Component
@MonitorCRT
public class Monitor17 implements Monitor {
// ...
}
@Component
@Monitor19
@MonitorLED
public class MonitorLED19 implements Monitor {
// ...
}
@Component
@Monitor22
@MonitorLED
public class MonitorLED22 implements Monitor {
// ...
Ao executarmos nossa aplicao agora tudo ir funcionar perfeitamente bem (ou pelo
menos deveria hehehe).
nesse momento que voc tem sua ltima epifania do post e pergunta: u, l na classe
Main.java ns buscamos o computador pelo ID dele, como o Spring pegou a classe
certa?.
Muito bem observado! hehehe
Quando o Spring busca e cria automaticamente os beans ele seta o ID de cada bean com
o nome da classe no padro CamelCase para identificadores. Com
isso Computador ficacomputador.
Mas caso haja a necessidade de especificar qual ser o ID do bean basta informar na
prpria anotao @Component:
@Component("pc")
public class Computador {
// ...
}
Concluso
E no fim do post temos um XML de configurao do Spring com apenas 1 linha!
No sei quanto a vocs mas eu acho isso fantstico, onde for possvel minimizar as
configuraes em XML melhor pois torna o cdigo bem mais legvel e fcil de entender.
Nossa aplicao
Vamos imaginar que voc trabalha em uma startup na rea de desenvolvimento de
software e o seu chefe te escolheu para criar uma pequena aplicao para ele poder ter
mais controle sobre a empresa.
Nessa empresa utilizado o Scrum como processo de desenvolvimento. Sempre antes de
ir embora os desenvolvedores acessam o sistema de controle da empresa para marcar
quais atividades esto concludas, quais esto em andamento e quais ainda esto
paradas.
Existem algumas mquinas que o chefe gosta de chamar de servidores (d um desconto
vai, est no comeo ainda) pois elas executam os papis de servidores de firewall, banco
de dados (onde roda o mysql) e de aplicaes (onde roda o tomcat).
O chefe quer chegar todo dia de manh e a primeira coisa que ele quer fazer verificar um
e-mail enviado pelo novo sistema com o andamento das tarefas do dia anterior, inclusive,
ele quer que no e-mail aparea em vermelho o nome das pessoas que no atualizaram o
status das tarefas no dia anterior pra ele poder ter uma conversinha com elas. hehehehe
Outra coisa que acontece bastante na empresa que os servidores costumar cair. Sempre
que isso acontece voc pensa: Eu j disse que aquelas mquina no so servidores! Viu
s! No aguentam a carga!.
Tudo bem, todos concordamos que essa situao no muito legal mas at a empresa
arranjar mais alguns clientes o chefe disse que no vai trocar nada. Por causa dessas
quedas frequentes ele tambm quer que toda vez que algum servidor pare de funcionar
ele receba um email avisando isso.
Uma ltima necessidade que a aplicao seja web e seja possvel o chefe saber qual
servidor parou, quando o e-mail foi enviado, enfim, um histrico de tudo que o seu
programa fez. Ele quer isso para que ele possa verificar tudo atravs do celular.
Recapitulando ento, a aplicao vai ser web, vai ter que enviar um e-mail bem cedo com
o andamento das tarefas do dia anterior e toda vez que algum servidor cair ele quer
receber um e-mail informando.
Legal, agora que j temos o nosso escopo vamos ao trabalho!
O projeto
No fim desse post teremos um projeto com essa estrutura:
Aps o projeto web ter sido criado e todas as dependncias corretamente referenciadas
vamos dar uma olhada no web.xml:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Apenas informamos o listener para carregar o Spring assim que a aplicao for iniciada e
definimos o arquivo para ser compatvel com servlets 3.0 .
O arquivo de configurao do Spring tambm no tem nenhum segredo:
<context:component-scan base-
package="br.blog.javasimples.spring.agendamento" />
<task:annotation-driven />
</beans>
Atravs da tag <context:component-scan> o Spring vai procurar sozinho pelos beans que
ele deve gerenciar (como explicado no post Injeo de dependncias via anotaes).
E atravs da tag <task:annotation-driven /> o Spring tambm vai procurar
automaticamente por tarefas agendadas e vai ficar responsvel pela execuo delas.
A interface Web
Nossa aplicao web vai usar uma estrutura bem simples (servlet + JSP) apenas para no
alongar muito o post.
A nica pgina que vamos desenvolver vai mostrar um histrico de tudo que a aplicao j
fez, vamos ver como ficaria a pgina index.jsp:
Um simples JSP que vai mostrar todos os registros de um histrico que um servlet ir
retornar. Vamos dar uma conferida no servlet:
/**
* Servlet para a recuperao do histrico de todas as tarefas executadas.
*
* @author Felipe
*/
@WebServlet(value="/historico")
public class HistoricoServlet extends HttpServlet {
@Autowired
private HistoricoExecucao historicoExecucao;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
config.getServletContext());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setAttribute("historico",
historicoExecucao.getHistorico());
RequestDispatcher dispatcher =
req.getRequestDispatcher("index.jsp");
dispatcher.forward(req, resp);
}
Toda vez que chegar uma requisio GET na URL /historico da nossa aplicao o servlet
vai carregar o histrico atravs do bean historicoExecucao e vai renderizar a
pginaindex.jsp para o usurio saber quais foram as ltimas tarefas executadas.
Por que a sobrescrita do mtodo init(ServletConfig)?
Bom nosso servlet possui um atributo que precisa ser injetado pelo Spring; o Spring s
consegue injetar dependncias em beans que esto sendo gerenciador por ele, ou seja, os
beans devem estar no contexto do Spring para terem suas dependncias injetadas; em
tempo de execuo quem vai instanciar o servlet o container web (ex: tomcat) e no o
Spring;
Sendo assim, quando o servlet instanciado ele no est no contexto do Spring e
consequentemente no ter suas dependncias injetadas. Para ter as suas dependncias
injetadas ns invocamos o mtodo esttico
processInjectionBasedOnServletContext(Object, ServletContext) da
classe SpringBeanAutowiringSupport para o Spring fazer esse favor para ns.
Vamos ver ento o que esse historicoExecucao faz:
/**
* Classe que contm o histrico de execuo de todas as tarefas.
*
* @author Felipe
*/
@Component
public class HistoricoExecucao {
public HistoricoExecucao() {
historico = new ArrayList();
simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy -
HH:mm:ss");
}
/**
* Classe utilitria para tarefas relacionadas a infra estrutura.
*
* @author Felipe
*/
public class InfraUtils {
Agora que j temos a estrutura para realizar um ping ou pelo menos simular um vamos
para as tarefas mesmo. Vamos comear criando uma interface para padronizar todas as
nossas tarefas:
/**
* Contrato que define os mtodos necessrios para uma tarefa.
*
* @author Felipe
*/
public interface Tarefa {
void executar(Object... args);
}
A nica coisa que uma Tarefa vai ter que fazer ser executada. Simples assim.
Agora que j sabemos como vai ser uma tarefa podemos criar a TarefaPingServidores:
/**
* Tarefa para pingar os servidores e verificar se esto ativos ou no.
*
* @author Felipe
*/
@Component
public class TarefaPingServidores implements Tarefa {
@Autowired
private HistoricoExecucao historicoExecucao;
@Override
public void executar(Object... args) {
if (args == null || args.length == 0) {
throw new IllegalArgumentException("A tarefa deve
receber o IP do servidor a ser pingado!");
}
if (InfraUtils.pingaServidor(ipServidor)) {
historico = nomeServidor + " pingado com sucesso.";
} else {
historico = nomeServidor + " caiu!";
EmailUtils.enviaEmail(EmailUtils.EMAIL_CHEFE,
historico);
}
historicoExecucao.novoHistorico(historico);
}
Nossa tarefa segue uma lgica um tanto quanto simples no? Garante que foi enviado o IP
do servidor a ser pingado, pega o nome do servidor a partir do seu IP, se no pingou com
sucesso, ou seja, se o servidor caiu envia um e-mail para o chefe e por fim registra o que
aconteceu no histrico.
Como envia o e-mail? Sinto muito mas no vai ser hoje que vamos ver hehehehe
/**
* Classe utilitria para o envio de emails.
*
* @author Felipe
*/
public class EmailUtils {
A anotao @Scheduled
Ok, temos a nossa primeira tarefa pronta. Mas como vamos fazer ela ser executada a
cada 5 segundos?
Vamos dar uma olhada na classe TarefasAgendadas que onde vo ficar todas os nossos
agendamentos para a execuo de tarefas:
/**
* Classe que contm todas as tarefas agendadas do sistema.
*
* @author Felipe
*/
@Component
public class TarefasAgendadas {
@Autowired
@Qualifier("tarefaPingServidores")
private Tarefa pingarServidores;
@Scheduled(fixedDelay=5000)
private void pingarServidores() {
String[] servidores = InfraUtils.IPS_SERVIDORES;
for (int i = 0; i < servidores.length; i++) {
pingarServidores.executar(servidores[i]);
}
}
}
Para o mtodo pingarServidores() ns definimos que a cada 5 segundos desde sua ltima
execuo ele dever ser invocado novamente. Com isso basta executarmos a aplicao,
esperar alguns segundos, clicar no link Recarregar e teremos uma sada parecida com
essa:
A anotao @Async
Depois que voc diz para o teu chefe que terminou a funcionalidade de pingar os
servidores ele pergunta: Mas o ping nos servidores est sendo feito de forma assncrona
certo?. Errado! Do jeito que est as chamadas esto sendo feitas de maneira sncrona
dentro do for, ou seja, para pingar o segundo servidor tem que esperar o ping do primeiro
terminar e assim por diante.
Mas por sorte a galera do Spring j pensou nisso e j disponibilizou uma anotao para
resolver o nosso problema: a anotao @Async.
Para tornarmos os pings assncronos basta fazermos o seguinte:
/**
* Classe responsvel pelas tarefas agendadas relacionadas a infra estrutura.
*
* @author Felipe
*/
@Component
public class TarefaPingServidores implements Tarefa {
@Autowired
private HistoricoExecucao historicoExecucao;
@Async
@Override
public void executar(Object... args) {
if (args == null || args.length == 0) {
throw new IllegalArgumentException("A tarefa deve
receber o IP do servidor a ser pingado!");
}
if (InfraUtils.pingaServidor(ipServidor)) {
historico = nomeServidor + " pingado com sucesso.";
} else {
historico = nomeServidor + " caiu!";
EmailUtils.enviaEmail(EmailUtils.EMAIL_CHEFE,
historico);
}
historicoExecucao.novoHistorico(historico);
}
/**
* Classe que contm todas as tarefas agendadas do sistema.
*
* @author Felipe
*/
@Component
public class TarefasAgendadas {
@Autowired
@Qualifier("tarefaPingServidores")
private Tarefa pingarServidores;
@Scheduled(fixedDelay=5000)
private void pingarServidores() {
String[] servidores = InfraUtils.IPS_SERVIDORES;
for (int i = 0; i < servidores.length; i++) {
pingarServidores.executar(servidores[i], i);
}
}
}
/**
* Classe responsvel pelas tarefas agendadas relacionadas a infra estrutura.
*
* @author Felipe
*/
@Component
public class TarefaPingServidores implements Tarefa {
@Autowired
private HistoricoExecucao historicoExecucao;
@Async
@Override
public void executar(Object... args) {
if (args == null || args.length == 0) {
throw new IllegalArgumentException("A tarefa deve
receber o IP do servidor a ser pingado!");
}
System.out.println(String.format("%s comeando
(%d)",Thread.currentThread().getName(), args[1]));
historicoExecucao.novoHistorico(historico);
System.out.println(String.format("%s terminando
(%d)",Thread.currentThread().getName(), args[1]));
}
/**
* Classe responsvel pelo envio agendado de emails.
*
* @author Felipe
*/
@Component
public class TarefaAndamentoDiarioSprints implements Tarefa {
@Autowired
private HistoricoExecucao historicoExecucao;
@Override
public void executar(Object... args) {
// Levanta os dados necessrios e monta a mensagem
String andamentoTarefas = "Segue o andamento das tarefas...";
EmailUtils.enviaEmail(EmailUtils.EMAIL_CHEFE,
andamentoTarefas);
Uma vez que temos a tarefa vamos agend-la para que seja enviada no ltimo segundo de
cada dia, ou seja, 23:59:59h. Para isso usaremos o atributo cron da anotao
@Scheduled:
/**
* Classe que contm todas as tarefas agendadas do sistema.
*
* @author Felipe
*/
@Component
public class TarefasAgendadas {
@Autowired
@Qualifier("tarefaPingServidores")
private Tarefa pingarServidores;
@Autowired
@Qualifier("tarefaAndamentoDiarioSprints")
private Tarefa andamentoDiarioSprints;
@Scheduled(fixedDelay=5000)
private void pingarServidores() {
String[] servidores = InfraUtils.IPS_SERVIDORES;
for (int i = 0; i < servidores.length; i++) {
pingarServidores.executar(servidores[i], i);
}
}
Expresses cron
Olhando para essa tabela agora j quase conseguimos ler a expresso que usamos na
anotao @Scheduled(cron =59 59 23 ? * *) J sabemos que vai ser no horrio
23:59:59, mas o que significam esses caracteres especiais? Vamos l:
Interrogao (?) utilizador quando o valor do campo no importa. Ex: quero que
seja disparada uma tarefa todo dia 10 mas no me importo qual dia da semana vai ser o
dia 10;
Hfen (-) utilizado para informar perodos. Ex: 1-4 no campo ms quer dizer que
a tarefa ser disparada em janeiro, fevereiro, maro e abril;
Vrgula (,) utilizado para valores adicionais. Ex: MON,WED,FRI no campo dia
da semana indica a execuo na segunda, quarta e sexta feiras;
W (weekday) usado para especificar o dia til (segunda sexta) mais prximo
do dia informado. Ex: 15W no campo dia do ms vai executar na segunda feira (dia 14)
caso o dia 15 caia em um sbado ou ento vai executar em uma segunda (16) caso o dia
15 seja um domingo. Se utilizarmos 1W e o primeiro dia do ms seja um sbado, s vai
executar na segunda feira (dia 3). S possvel utilizar esse caractere quando o dia do
ms for um nico dia, sua utilizao com perodos no permitida;
Sustenido (#) utilizado para especificar o n-simo dia do ms. Ex: 6#3 no
campo dia da semana significa a terceira sexta feira do ms (6 = sexta, #3 = terceira);
4#5 significa a quinta quarta feira do ms, porm se no houver uma quinta quarta feira
no ms a tarefa no ser executada.
Obs: Os caracteres L e W podem ser utilizados em conjunto no campo dia do ms. Ex:
LW significa o ltimo dia til do ms.
Bom, expresses cron podem ser fceis como 0 0 15 ? * * ou podem ser complexas
como 0/5 14,18,3-39,52 * ? JAN,MAR,SEP MON-FRI 2002-2010.
Mas ento como saber se a sua expresso est certa ou no?
Para isso existe uma classe bem bacana na biblioteca do Quartz: CronExpression.java.
Nela existe o mtodo getNextValidTimeAfter(Date) que nos retorna a prxima data (a partir
do parmetro) na qual a tarefa ser executada. Com isso ns podemos montar uma classe
utilitria que nos permite testar se nossa expresso est certa ou no:
/**
* Classe responsvel por realizar testes com as Cron Expressions.
*
* @author Felipe
*/
public class TestaExpressao {
Obs: para essa classe funcionar ser necessrio colocar a biblioteca do Quartz no
classpath.
O mtodo mostrarProximasDatas(String, int) recebe uma expresso e a quantidade de
datas futuras em que essa expresso ir disparar sero exibidas como parmetros. Ao
executar o exemplo acima vamos ter uma sada parecida com essa:
Vemos que vai disparar todo dia exatamente s 23:59:59, ou seja, conferimos que nossa
expresso vai fazer exatamente o que queremos!
Concluso
Bom pessoal, depois desse pequeno post voc vai conseguir entregar a aplicao para o
seu chefe antes do prazo e ele vai ficar muito satisfeito com o resultado! hehehe
Mas sem brincadeiras, o Spring prov uma forma maneira muito fcil e ao mesmo tempo
muito poderosa para agendarmos tarefas. Sem falar que incrivelmente legvel n (devido
ao uso de anotaes e poucas configuraes em XML).
Caso a sua aplicao seja muito grande mesmo e precise de tarefas agendadas como
realizar transaes bancrias (ou coisas de mesma importncia) eu aconselharia dar uma
olhada no Quartz mesmo. Ele prov algumas funcionalidades a nvel de aplicaes de
grande porte como persistncia dos jobs a serem executados (em caso de alguma falha),
clusterizao e suporte a transaes JTA.
A idia de criar este post foi do Felipe Saab quando sugeri a ele manter o post Spring
Framework Parte 4 -> Integrao com o Hibernate atualizado para uma verso mais nova
das bibliotecas.
Porm essa atualizao iria modificar uma parte da codificao do post ento decidimos
criar um novo focando somente em Spring 3.1 e Hibernate 4.0.
Aos que esto iniciando neste assunto, sugiro a leitura dos assuntos abordados em posts
anteriores:
Bibliotecas e Dependncias
Criarei no projeto uma pasta lib e colocarei dentro as bibliotecas necessrias para rodar o
Spring 3.1 e o Hibernate 4.0. As bibliotecas podem ser encontradas em:
O Spring e o Hibernate fazem uso de bibliotecas auxiliares que precisaro ser adicionadas:
Aps adicionar os jars ao projeto devemos ter uma tela parecida com essa:
Mos obra
Vamos definir uma estrutura inicial de pacotes para o nosso projeto:
Arquivo de configurao
Vamos criar o arquivo de configurao do Spring com o nome conf.xml no pacote conf:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost/hibernate_teste" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>spring_hibernateannotations.modelo.Livro</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop
key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
</beans>
Banco de Dados
Vamos neste post seguir a estrutura de banco do post anterior, ou seja, vamos utilizar a
classe Livro e a tabela Livros.
Segue o script para criar a tabela no MySQL (conforme publicao anterior do Felipe
Saab):
@Entity
@Table(name = "livros")
@GenericGenerator(name = "mygenLivros", strategy = "increment")
public class Livro implements Serializable {
@Id
@GeneratedValue(generator = "mygenLivros")
@Column(name = "liv_cod")
private int cod;
@Column(name = "liv_titulo")
private String titulo;
// Getters e Setters
}
Se vocs repararem bem, uma simples classe com getters e setters, porm com
algumas coisas a mais:
implements Serializable: Uma boa prtica fazer com que todas as entidades mapeadas
(@Entity) implementem a interface java.io.Serializable pois em quase todas as aplicaes
web as entidades so serializadas do servidor para o cliente. Essa implementao garante
que nenhum dado se perca durante a serializao.
Campo esttico serialVersionUID: A serializao necessita de um identificador da classe
(mais detalhes em http://blog.caelum.com.br/entendendo-o-serialversionuid/). Uma
possibilidade ger-lo utilizando o prprio eclipse:
Hibernate Annotations
Caso voc no saiba o que so anotaes aconselho a leitura do post Annotations: O que
, pra que serve?.
So as informaes (metadados) que o Hibernate utilizar para associar esta classe
tabela do banco de dados. Acredito que neste ponto alguns desenvolvedores j podem
imaginar a econmia de cdigo que isso pode trazer. Explicarei abaixo cada uma desta
anotaes, porm entendam que existem mais anotaes, o que torna o Hibernate flexvel
para trabalhar com a maioria dos modelos de dados relacionais existentes.
o name: Nome que identifica o gerador (para ser utilizado como referncia
em outras anotaes);
Implementando a aplicao
Vamos criar agora a classe que implementa os mtodos CRUD do Livro. Na
packagespring_hibernateannotations.interfaces ser criada a interface IBanco.java. Em
nosso exemplo esta interface no parece muito til, mas em um projeto maior podemos
querer obrigar que toda classe que implementa a interface com banco de dados
implemente as mesmas assinaturas de mtodos.
return livro;
try {
if(!sessao.isOpen()) {
sessao = sessionFactory.openSession();
}
transacao = sessao.beginTransaction();
sessao.update(livro);
transacao.commit();
}
catch(HibernateException e)
{
System.out.println("Erro: " + e.getMessage());
transacao.rollback();
}
finally {
try {
if(sessao.isOpen()) {
sessao.close();
}
}
catch(Throwable e){
System.out.println("Erro ao finalizar
atualizao: " + e.getMessage());
}
}
}
public void ApagarLivro(Livro livro) {
try {
if(!sessao.isOpen()) {
sessao = sessionFactory.openSession();
}
transacao = sessao.beginTransaction();
sessao.delete(livro);
transacao.commit();
}
catch(HibernateException e) {
System.out.println("Erro: " + e.getMessage());
transacao.rollback();
}
finally {
try {
if(sessao.isOpen()) {
sessao.close();
}
}
catch(Throwable e) {
System.out.println("Erro ao finalizar : " +
e.getMessage());
}
}
}
livro1 = banco.InserirLivro(livro1);
livro2 = banco.InserirLivro(livro2);
System.out.println("Livros gravados no banco");
System.out.println("Atualizando livro");
livro1.setTitulo(livro1.getTitulo()+" Segunda Edio");
banco.AtualizarLivro(livro1);
System.out.println("Apagando livro");
banco.ApagarLivro(livro2);
}