Você está na página 1de 7

1.

Spring Framework
1.1. Introduo
Neste captulo iremos converter o sistema Financeiro desenvolvido nos captulos anteriores para que se use o Spring Framework. Antes de iniciar a converso, essencial que os conceitos de Inversion of Control (inverso de controle) e Dependecy Injection (injeo de dependncia) estejam claros. No treinamento Tecnologias_no_Fmk_Sfw_v1 explicamos ambos os conceitos com maiores detalhes.

1.2. Configurao do Spring Framework


O primeiro passo para utilizarmos o Spring configurarmos o Eclipse. No projeto Financeiro, copie as bibliotecas que esto no diretrio libs-spring para o diretrio WEB-INF/lib dentro de WebContent. Em seguida, crie o arquivo applicationContext.xml no diretrio WEB-INF com o seguinte contedo:
<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" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" default-lazy-init="true"> <context:annotation-config/> <context:component-scan base-package="com.algaworks.dwjsf.financeiro."/> </beans>

Devemos configurar o Tomcat para carregar o container do Spring na inicializao do servidor. Para isso iremos configurar o arquivo web.xml que fica no diretrio WEB-INF:
<web-app ...> <!-- configure este parmetro caso seu arquivo de configurao no esteja no diretrio default com o nome de applicationContext.xml <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/configuracao.xml</param-value> </context-param> --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> ... </web-app>

Por default, o Spring ir carregar o arquivo de configurao applicationContext.xml dentro do diretrio WEB-INF. Porm possvel alterar esta configurao atravs do parmetro contextConfigLocation como indicado no trecho acima. Aps esta configurao, ao subirmos o Tomcat, no console aparecer a seguinte linha indicando que o container do Spring foi carregado: INFO: Initializing Spring root WebApplicationContext

1.3. Integrando o Spring ao JPA


Para integrarmos o Spring ao JPA alteraremos o arquivo de configurao applicationContext.xml. Adicione o no arquivo o seguinte trecho:
<tx:annotation-driven /> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="financeiro-persistence-unit" /> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

O Spring fornece diversas classes que auxiliam na integrao com o JPA. A classe LocaEntityManagerFactoryBean cria uma factory para criao do EntityManagerFactory. Apenas com esta tag j seria possvel injetar o EntityManagerFactory em um Spring Bean. Note que o valor da propriedade persistenceUnitName o nome do persistence unit definido no arquivo persistence.xml (financeiro-persistence-unit). A classe JpaTransactionManager com a tag <tx:annotation-driven /> habilitam a transao declarativa do Spring. Assim podemos utilizar a anotao @Transactional para controlarmos as transaes de banco. A classe PersistenceAnnotationBeanPostProcessor permite a injeo do EntityManager atravs da anotao @PersistenceContext nos Spring Beans.

1.4. Integrando o Spring ao JSF


Para integrarmos o Spring ao JSF iremos alterar os arquivos web.xml e facesconfig.xml. No web.xml adicionamos o seguinte cdigo:
<listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener>

A classe RequestContextListener expe a requisio HTTP para o Spring. No arquivo faces-config.xml inclua cdigo:
<application> <el-resolver> org.springframework.web.jsf.el.SpringBeanFacesELResolver </el-resolver> </application>

A classe SpringBeanFacesELResolver tem a tarefa de resolver os nomes dos Spring Beans no container Spring que foi integrado ao Tomcat. Remova do faces-config.xml as declaraes dos managed beans cadastroContaBean e consultaContaBean.

1.5. Convertendo os EAOs em Spring Beans


No captulo anterior criamos uma classe Factory para a criao dos EAOs (JpaPessoaEAO e JpaContaEAO). Utilizando a integrao do JPA com o Spring, poderemos excluir as classes: EAOFactory, JpaEAOFactory e JpaUtil. Essas classes eram responsveis por criar e recuperar a conexo com o banco de dados, criar instncias dos

EAOs e por controlar as transaes com o banco. Todas estas atividades sero controladas pelo Spring. Edite as classes JpaPessoaEAO e JpaContaEAO anote-as com a anotao @Named. Remova tambm o construtor que recebe um EntityManager como parmetro.
@Named public class JpaPessoaEAO extends JpaGenericEao<Pessoa> implements PessoaEAO { public JpaPessoaEAO(){ }

@Named public class JpaContaEAO extends JpaGenericEao<Conta> implements ContaEAO { public JpaContaEAO(){ }

Com a anotao @Named estamos informando ao container Spring que a classe JpaPessoaEAO e JpaContaBean so Spring Beans, ou seja, so objetos gerenciados pelo container. Edite a classe JpaGenericEAO.
package com.algaworks.dwjsf.financeiro.eao.jpa; //imports @Named public class JpaGenericEao<T> implements GenericEAO<T> { private Class<T> persistentClass; @PersistenceContext private EntityManager entityManager; public JpaGenericEao() { this.persistentClass = (Class<T>)((ParameterizedType) getClass().getGenericSuperclass()) .getActualTypeArguments()[0]; } ... @Transactional public T salvar(T entidade) { return this.entityManager.merge(entidade); } @Transactional public void excluir(Serializable id) { T entity = this.pesquisarPorId(id); this.entityManager.remove(entity); } }

Adicione a anotao @Named na classe JpaGenericEAO. Isso indica que a classe ser um SpringBean. Remova o parmetro EntityManager em do construtor da classe. No atributo de classe entityManager adicione a anotao @PersistenceContext. Isso injetar na classe uma instncia da classe EntityManager. Anote os mtodos salvar() e excluir() com a anotao @Transactional. Desta forma o controle da transao ficar por conta do Spring. O container saber quando fazer o commit ou o rollback da transao.

1.6. Convertendo as classes de negcio em Spring Beans


Edite a classe PessoaService:
package com.algaworks.dwjsf.financeiro.negocio;

// imports @Named public class PessoaService { @Inject private PessoaEAO pessoaEAO; public Pessoa pesquisarPorId(Long id) { Pessoa pessoa = pessoaEAO.pesquisarPorId(id); return pessoa; } public List<Pessoa> listarTodas() { List<Pessoa> pessoas = pessoaEAO.listarTodosOrdenadoPorNome(); return pessoas; } public PessoaEAO getPessoaEAO() { return pessoaEAO; } public void setPessoaEAO(PessoaEAO pessoaEAO) { this.pessoaEAO = pessoaEAO; } }

Anote a classe com @Named para exp-la ao Spring. Crie um atributo de instncia PessoaEAO e anote-o com @Inject. Isto indica ao Spring que ele deve criar uma instncia de um Spring Bean que implementa a interface PessoaEAO (no nosso caso JpaPessoaEAO) e injetar esta instncia no atributo pessoaEAO. Substitua todas referncias do EAOFactory do servio, pois quem faz o papel da factory o Spring. Faa o mesmo processo com a classe ContaEAO. Segue o cdigo com as alteraes.
package com.algaworks.dwjsf.financeiro.negocio; //imports @Named public class ContaService { @Inject private ContaEAO contaEAO; public void salvar(Conta conta) throws RegraNegocioException { if (conta.getValor().compareTo(BigDecimal.ZERO) <= 0) { throw new RegraNegocioException( "Valor da conta deve ser maior que zero."); } contaEAO.salvar(conta); } public Conta pesquisarPorId(Long id) { Conta conta = contaEAO.pesquisarPorId(id); return conta; } @SuppressWarnings("unchecked") public List<Conta> listarTodas() { List<Conta> contas = contaEAO.listarTodasOrdenadasPelasMaisAntigas(); return contas; } public void excluir(Conta conta) throws RegraNegocioException { if (conta.getDataBaixa() != null) { throw new RegraNegocioException( "Esta conta no pode ser excluda, pois j foi " + "baixada!"); } contaEAO.excluir(conta.getId());

} public List<String> pesquisarDescricoes(String descricao) { List<String> descricoes = contaEAO.pesquisarDescricoes(descricao); return descricoes; } }

1.7. Convertendo os managed beans em Spring Beans


Na nossa aplicao de exemplo, os managed beans tambm sero convertidos em Spring Beans. Isso garante uma maior padronizao na forma de trabalharmos com as classes. Todas as camadas da aplicao so declaradas praticamente da mesma forma, ou seja, com a anotao @Named e criadas com a anotao @Inject. Edite a classe ConsultaContaBean.
package com.algaworks.dwjsf.financeiro.visao; //imports @Named("consultaContaBean") @Scope("session") public class ConsultaContaBean { private Conta contaExclusao; private List<Conta> contas = new ArrayList<Conta>(); @Inject private ContaService contaService; public void consultar(ActionEvent event) { this.contas = contaService.listarTodas(); } public String excluir() { FacesContext context = FacesContext.getCurrentInstance(); try { contaService.excluir(this.contaExclusao); this.contas.remove(this.contaExclusao); this.contaExclusao = null; FacesMessage msg = new FacesMessage( "Conta excluda com sucesso!"); msg.setSeverity(FacesMessage.SEVERITY_INFO); context.addMessage(null, msg); } catch (RegraNegocioException e) { context.addMessage( null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e .getMessage(), e.getMessage())); } catch (Exception e) { e.printStackTrace(); FacesMessage msg = new FacesMessage( "Erro inesperado ao excluir conta!"); msg.setSeverity(FacesMessage.SEVERITY_ERROR); context.addMessage(null, msg); } return null; } public List<Conta> getContas() { return contas; } public Conta getContaExclusao() { return contaExclusao; } public void setContaExclusao(Conta contaExclusao) { this.contaExclusao = contaExclusao; } }

Anote a classe com a anotao @Named. Porm, no caso de managed beans, atribua um nome ao Spring Bean. Este nome ser utilizado nas pginas jspx. Adicione a anotao @Scope. Neste caso, o escopo do managed bean de sesso. Crie um atributo de instncia do tipo ContaService e anote-o com a anotao @Inject. Remova todas as instanciaes da classe ContaService via new, e substitua pelo atributo contaService declarado anteriormente. Repita a operao para a classe CadastroContaBean. Segue o cdigo alterado:
package com.algaworks.dwjsf.financeiro.visao; //imports @Named("cadastroContaBean") @Scope("session") public class CadastroContaBean { private Conta contaEdicao; private List<SelectItem> tiposContas; private List<SelectItem> pessoas; @Inject private ContaService contaService; @Inject private PessoaService pessoaService; public String inicializar() { this.contaEdicao = new Conta(); this.tiposContas = null; this.pessoas = null; return "cadastroConta"; } public void salvar(ActionEvent event) { FacesContext context = FacesContext.getCurrentInstance(); try { contaService.salvar(this.contaEdicao); this.contaEdicao = new Conta(); FacesMessage msg = new FacesMessage("Conta salva com sucesso!"); msg.setSeverity(FacesMessage.SEVERITY_INFO); context.addMessage(null, msg); } catch (RegraNegocioException e) { context.addMessage( null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e .getMessage(), e.getMessage())); } catch (Exception e) { e.printStackTrace(); FacesMessage msg = new FacesMessage( "Erro inesperado ao salvar conta!"); msg.setSeverity(FacesMessage.SEVERITY_ERROR); context.addMessage(null, msg); } } public List<SelectItem> getPessoas() { if (this.pessoas == null) { this.pessoas = new ArrayList<SelectItem>(); List<Pessoa> pessoas = pessoaService.listarTodas(); this.pessoas.add(new SelectItem(null, "Selecione")); for (Pessoa pessoa : pessoas) { this.pessoas.add(new SelectItem(pessoa, pessoa.getNome())); } } return this.pessoas; } public List<SelectItem> getTiposLancamentos() { if (this.tiposContas == null) { this.tiposContas = new ArrayList<SelectItem>(); for (TipoConta tipo : TipoConta.values()) { this.tiposContas.add( new SelectItem(tipo, tipo.toString())); } } return tiposContas;

} public List<String> sugerirDescricao(Object event) { return contaService.pesquisarDescricoes(event.toString()); } public Conta getContaEdicao() { return contaEdicao; } public void setContaEdicao(Conta contaEdicao) { this.contaEdicao = contaEdicao; } }

Por ltimo, altere a classe PessoaConverter. Ela utiliza o servio PessoaService. Porm, no podemos simplesmente instanciar um servio utilizando new. Portanto, iremos obter o servio buscando-o no container Spring. Para isso altere o mtodo getAsObject da classe como abaixo:
public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException { if (value == null) { return null; } PessoaService pessoaService = FacesContextUtils. getWebApplicationContext(context). getBean(PessoaService.class); return pessoaService.pesquisarPorId(Long.parseLong(value)); }

O mtodo FacesContextUtils.getWebApplicationContext(contex) recupera o contexto do Spring integrado ao Tomcat e atravs dele possvel obter uma instncia da classe PessoaService. No h alteraes nas pginas jspx.