Escolar Documentos
Profissional Documentos
Cultura Documentos
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.
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
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.
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.
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.
// 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());
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.