Como integrar JSF e Spring

Postado por em   ●   29 comentários

Gostaria de utilizar o melhor dos dois mundos?

Não é porque o JSF está naturalmente integrado com outras especificações Java EE, que é proibido utilizar ele com Spring.

Por que não utilizar o melhor do JSF com o melhor do Spring? Assim podemos ter a produtividade do JSF junto com todos os recursos do ecossistema Spring.

Um não exclui o outro. Pelo contrário, eles são perfeitamente integráveis e veremos agora como fazer isso.

Entre outras coisas, nesse artigo, você vai aprender:

  • Por que integrar JSF e Spring
  • Quais os benefícios dessa integração
  • Como integrar JSF e Spring para dar aos beans do Spring Framework o papel de um Managed Bean
  • A adicionar o ViewScope no Spring

Interessado? Vamos lá então.

Por que integrar JSF e Spring?

Uma pequena desvantagem nessa integração é o fato do Spring ainda não possuir, nativamente, o escopo de view.

Só que, como o Spring nos dá a possibilidade de criar escopos customizados, esse é um problema que podemos resolver sem gambiarras e até com elegância, eu diria.

Sobre a outra configuração que precisa ser feita no faces-config.xml (ainda falaremos dela nesse artigo), não acho que isso deva ser uma desvantagem de peso na decisão de se utilizar JSF e Spring.

Muitos preferem JSF com CDI, pois eles se integram naturalmente, mas se olharmos para o todo, as coisas não ficam muito mais simples do que utilizar JSF com Spring.

Pense comigo, quem usa o JSF com CDI:

  • Está utilizando um servidor de aplicações que, apesar dos seus benefícios, carregam suas complexidades; ou
  • Tem que adicionar configurações extras para fazê-lo funcionar em um container de servlet qualquer (Tomcat, por exemplo).

Ou seja, o ambiente como um todo não fica mais ou menos complexo porque você utilizou Spring ou CDI.

O que mais deve pesar na decisão de usar JSF com Spring é o que você pensa sobre o futuro:

  • Suas próximas aplicações serão baseadas em Spring?
  • Você pretende migrar as aplicações existentes para Spring?

Existe outro ponto importante (e que influencia nas respostas para as perguntas acima), que é o seu domínio (ou o domínio do seu time) sobre a tecnologia.

O que não pode ter o maior peso é querer usar JSF com CDI só porque eles fazem parte de uma especificação, digamos, “formal” do Java EE. Isso pode ser levado em conta, claro, mas não acho que deve ser a característica que vai decidir.

Quais os benefícios de se integrar JSF e Spring

Não preciso dizer muito sobre os benefícios aqui.

Em um artigo anterior, o Normandes falou especificamente sobre Spring e as qualidades do mesmo.

Ele deu uma definição bem bacana para o framework que já embute também as vantagens:

O Spring é composto por diversos projetos que ajudam os desenvolvedores a criarem aplicações Java mais simples, rápidas e flexíveis!

Falando de benefícios ligados diretamente aos Managed Beans do JSF, o Spring vai adicionar características importantes, como:

  • Injeção de dependências flexível;
  • Profiles (produção, desenvolvimento, testes e qualquer outro que quiser);
  • Gerenciamento de transações;
  • Suporte a AOP;
  • Entre outras coisas.

…que são interessantes para um desenvolvimento organizado e com produtividade.

Claro que temos funcionalidades semelhantes entre JSF e CDI também, mas nesse artigo aqui estou focando em Spring.

Entendendo a classe ELResolver

Para que você entenda como a integração funciona, vamos olhar o recurso do JSF que o Spring se utilizou para fazê-la.

Acredito que assim você terá um domínio maior sobre a funcionalidade.

Todas as vezes que o JSF encontra uma expression language como:

…ele delega a resolução dela para alguma classe que estenda a classe abstrata ELResolver (da API de Expression Language).

Interessante notar que não existe somente uma implementação de ELResolver, são várias e cada uma com seu objetivo.

Temos implementações que ajudam a resolver expressões que usam mapas, listas, propriedades com escopos específicos (como o Flash Scope), entre outros.

Dentro do JSF (Mojarra, especificamente) temos exemplos desse tipo de classe:

  • ManagedBeanELResolver;
  • ResourceELResolver;
  • ScopedAttributeELResolver;

Veja mais:

Implementações de javax.el.ElResolver

São essas classes as responsáveis por fazer com que cada parte da EL seja processada.

Para ficar mais claro como uma EL é resolvida, vamos olhar dentro da classe ELResolver. O método que vamos analisar é o getValue.

Veja:

A primeira coisa que o JSF tem que fazer é resolver a raiz de uma EL. Raiz essa que, na grande maioria das vezes, é um Managed Bean.

Tomando como exemplo a EL que mostrei acima, primeiro teria que ser resolvido o valor de clienteBean.

Para resolver a raiz da nossa EL, o método getValue (de uma classe concreta qualquer que estenda ELResolver) receberia um contexto de EL (do tipo ELContext), o parâmetro base como nulo (afinal, agora estamos resolvendo a base) e o parâmetro property como clienteBean (em string).

O que a classe deve fazer é retornar um objeto que seja referente a string “clienteBean”. Nesse exemplo seria uma instância da classe ClienteBean.

Resolvido o valor de clienteBean, agora o método getValue (da mesma classe concreta ou de outra) vai receber o mesmo contexto EL, o parâmetro base com o valor referente ao que foi encontrado para clienteBean e o parâmetro property com o valor igual a cliente.

Esse ciclo vai continuar até que a propriedade da ponta (nome, no caso) seja resolvida.

Voltando aqui sobre a integração entre JSF com Spring, no nosso caso de querer Managed Beans instanciados pelo Spring, basta criar uma implementação de ELResolver que busque a propriedade clienteBean dentro do application context do Spring.

Para nossa conveniência, o Spring já nos dá essa implementação.

Ele tem uma classe que se chama SpringBeanFacesELResolver, que estende de SpringBeanELResolver, e essa sim, estende ELResolver.

O que precisamos entender agora é como avisar o JSF que temos uma implementação de ELResolver que vai tornar os nossos beans Spring acessíveis via EL.

Integrando Spring com JSF

Quando temos um ELResolver, seja ele qual for, informamos ao JSF da existência do mesmo através da tag el-resolver dentro do faces-config.xml:

No exemplo acima, configuramos justamente a implementação do Spring para a classe ELResolver, que será responsável por retornar os beans Spring para o JSF.

Agora, ao invés de usar Managed Beans JSF, você vai usar beans Spring.

Nossa classe ClienteBean, que era um Managed Bean JSF:

…vai passar a ser um bean Spring:

Dessa forma, temos um bean Spring fazendo o papel de Managed Bean do JSF.

De resto, você vai continuar usando o Spring e JSF normalmente. O uso nas páginas JSF continua o mesmo. As EL’s não precisam ser alteradas.

Adicionando o suporte ao ViewScope nos beans Spring

Um dos escopos mais usados no JSF 2.x é o de view. Esse escopo não existe no Spring, mas podemos criá-lo facilmente.

No Spring temos um recurso que nos permite criar escopos customizados para ele. Vamos utilizar esse recurso para adicionar o escopo de view no Spring

O legal é que nem precisamos nos preocupar com o ciclo de vida dos beans, pois, vamos utilizar o viewMap do próprio JSF, que já faz esse gerenciamento.

Observe como fica simples o nosso escopo:

O que é necessário agora é avisar ao Spring que criamos esse escopo.

Para o registro, basta invocarmos o método registerScope da interface ConfigurableBeanFactory:

Existem várias formas de chegar a esse método, mas vamos utilizar uma disponibilizada pelo Spring, que é através da classe CustomScopeConfigurer.

Se você usa Spring com XML, então pode fazer assim:

Caso use a configuração via anotações, então pode usar:

Repare que o método acima é estático. Isso foi por uma recomendação do próprio framework, pois a classe CustomScopeConfigurer é um BeanFactoryPostProcessor.

É melhor que seja assim para evitar problemas com o processamento de alguma anotação como, por exemplo @Autowired, que esteja dentro de ViewScopeConfig. Em nosso caso, não tem, mas como é recomendado pelo framework, então eu utilizei o modificador static.

Agora, nosso ClienteBean pode ser configurado assim:

E quando o JSF tentar resolver a base da EL:

…ele vai pedir nosso ClienteBean para a classe SpringBeanFacesELResolver que, através de BeanFactory, vai encontrá-la em nossa classe ViewScope.

Assim temos o JSF integrado ao Spring com suporte para o escopo de view.

Pequeno aviso sobre usar Spring com JSF

Quando for integrar as duas tecnologias, como em nosso código de exemplo, pode ser que você veja uma mensagem como:

A primeira vista parece ser algum problema com a integração, mas é, simplesmente, uma INFO dizendo que não foi encontrado o gerenciador de @ViewScope do CDI.

Fique tranquilo, pois, não terá impactos na sua aplicação.

A versão do JSF que utilizamos no exemplo do artigo foi a 2.2.9:

Conclusão

Aprofundamos no entendimento sobre a classe abstrata ELResolver para mostrar qual a forma utilizada pelo Spring para integração das duas tecnologias.

Mostrei também como é feita a configuração da classe SpringBeanFacesELResolver dentro do faces-config.xml.

Por último, configuramos o escopo de view dentro do Spring para termos suporte equivalente ao da anotação @ViewScope do JSF.

No caso de querer se aprofundar no assunto de JSF, tenho uma boa recomendação pra você!

Para aprender mais sobre JSF, você pode baixar agora o e-book que o Thiago escreveu.

E-Book Java EE 7 com JSF, Primefaces e CDI

Depois do download, vai ser bacana se deixar seu comentário dizendo se gostou, se não gostou, uma opinião, crítica ou elogio.

Um abraço pra você e até uma próxima!

PS: Você pode acessar o código-fonte desse artigo com os exemplos no GitHub: http://github.com/algaworks/artigo-integracao-jsf-spring

É graduado em Sistemas de Informação, trabalha como instrutor na AlgaWorks e está no mercado de programação Java há mais de 8 anos, principalmente no desenvolvimento de sistemas corporativos.

29 comentários sobre “Como integrar JSF e Spring

  1. Eduardo Branquinho -

    Bom texto Alexandre!
    Primeiro post seu que vejo por aqui, seja bem vindo :)

    1. Alexandre Afonso Autor do post-

      Valeu Eduardo!
      É o primeiro mesmo… E vamos com tudo :)

  2. Masyaf saihttam -

    Parabéns por compartilhar conosco este conteúdo de grande valia. Seja bem vindo a algaworks e continue publicando artigos interessantes como este!

  3. Alexandre Afonso Autor do post-

    Bacana que curtiu, Masyaf :)
    Abraço!

  4. Ricardo Johannsen -

    Gostei muito do Post, antes do advento do CDI a Stack de quem programava em JSF quando não era JSF + SEAM era na maioria JSF + Spring + Hibernate e sempre se mostrou muito eficiente. Tenho uma dúvida quanto a esta implementação de View Scope com spring quando usado junto com o JSF. No View Scope padrão do CDI ou Jsf o escopo View não morre imediatamente após a troca de página, ele guarda em um mapa com um limite de 25 beans por usuário me parece(mojara) e só ai vai liberando a memória, mas pior mesmo aconte quando navegamos por get ou quando o usuário fecha a aba ou o navegador, nesses casos só o timeout vai matar o bean. Usando essa implementação de View Scope que você ensinou, quando ele é removido da memória na navegação via post, navegação via get e quando fecha o Browser?

  5. Alexandre Afonso Autor do post-

    Que bacana que gostou Ricardo.

    Legal que perguntou porque me dá a oportunidade de dizer algo que não falei no artigo:

    Essa implementação que utilizei é a mesma que o Mojarra usa para armazenar os Managed Beans que são @ViewScoped (veja ViewScopeContextManager no projeto Mojarra).

    Pensando no Mojarra (implementação que usei nesse artigo), os beans são removidos da memória em três casos.

    Um caso é quando há postback com navegação.

    O outro é nesse aí, que você comentou, de ultrapassar 25 views (na prática, são 25 abas do browser abertas). Repare que são 25 views e não beans (beans podemos ter quantos quisermos).

    O terceiro e mais óbvio, é quando a sessão expira.

    Importante dizer que não existe documentação formal sobre isso. A partir de pesquisas na internet e depuração de código eu separei mitos e verdades.

    Abraços!

  6. Carlos Alberto -

    Gostei do artigo, parabéns. Estou tentando integrar o Spring Boot com o Jsf 2.2 e Primefaces. Alguma sugestão?

  7. Alexandre Afonso Autor do post-

    Valeu Carlos!

    Tem um projeto em que a gente usa essa combinação. O projeto era um web comum. O que nós fizemos foi, simplesmente, tratar esse projeto web como um projeto Spring Boot. Ou seja, adicionamos o boot no path, criamos a classe main e adicionamos o FacesServlet através da criação de um bean do tipo “ServletRegistrationBean”. Quanto ao PrimeFaces não foi preciso fazer coisa alguma. Ele continuou funcionando da mesma forma.

    Abraços!

  8. Weliff Lima -

    Opa, excelente artigo.

    Uma dúvida: Com o Spring ele só precisa resolver a base da expressão? Como ele resolve as outras propriedades?

  9. EDER LUCIANO -

    Excelente Post, eu estou tentando exatamente isso, integrar spring e jsf e junit através de anotações, infelizmente sem muito sucesso caso possa me dar umas dicas eu agradeço.

  10. Alexandre Afonso Autor do post-

    Bacana que gostou Weliff!

    É a implementação de EL do Spring que precisa resolver somente a base da expressão.

    O JSF não teria como resolver a base da expressão, justamente, porque ela (a base) representa um bean Spring.

    Depois que a classe SpringBeanFacesELResolver resolve a base, então o JSF dá conta do resto :)

    Abraços!

  11. Alexandre Afonso Autor do post-

    Obrigado Eder!

    Você pode seguir dois caminhos:

    1. Fazer o projeto funcionar sem o Spring, simplesmente, para isolar a possibilidade de erros com essa configuração. Aí, depois de funcionando, você configura o Spring.

    2. Baixe o código fonte de exemplo do artigo e inclua suas classes nele. O exemplo usa somente anotações e configuração via Java.

    Espero que dê certo!

    Abraço!

  12. Arilson Santos -

    Bem bacana o post! Praticamente novo em folha rs
    Eu já fiz integração Spring (3) com JSF … por incrivel que pareça eu achava simples utilizar os XMLs de configuração. rs

    Agora estou aprendendo um pouco do Spring Boot e vou tentar fazer essa integração (para efeito de conhecimento mesmo, pois acho que vou optar pelo Action Based desta vez – para o que pretendo desenvolver ).

    Parabéns pelo post.

    1. Alexandre Afonso Autor do post-

      Obrigado Arilson!

      Com certeza, Spring Boot pode ajudar bastante mesmo, ainda que no desenvolvimento com JSF.

      Abraço!

  13. Claudio -

    Estava procurando um artigo com essa abordagem “interação do JSF com Spring” e que não possua muitas “gambiarras” para criação de escopos customizados. Parabéns, não achava que era muito fácil assim.

    1. Alexandre Afonso Autor do post-

      Bacana que gostou Claudio!

      É… A integração dos dois é quase natural heh

      Abraço!

  14. Injeção de dependências com Spring -

    […] Você ainda pode criar os seus próprios escopos caso precise. Assim como fizemos na configuração do escopo de “view” utilizado na integração do JSF com o Spring. […]

  15. Anderson Dias -

    O meu ta dando um erro, to tentando rodar no tomcat e lança a exceção :
    javax.servlet.ServletException: /registro-cliente.xhtml @45,7 value=”#{registroClienteBean.cliente.nome}”: Target Unreachable, identifier ‘registroClienteBean’ resolved to null
    javax.faces.webapp.FacesServlet.service(Unknown Source)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

    pensei que fosse a versão do mojarra que estava usando mais ai coloquei a mesma versão do artigo e o erro persiste.

  16. Anderson Dias -

    Descobri o erro, é que criei um projeto diferente do artigo com as calsses dividas em seus respectivos pacotes e a classe JsfSpringWebApplicationInitializer não estava na raiz dos pacotes, passei ela pra raiz e tudo funcionou normal. Erro bobo mas acontece, vlw.

  17. Alexandre Afonso Autor do post-

    Bacana que deu certo!

    Abraço Anderson.

  18. Anderson Dias -

    Queria pedir uma ajuda pois ja tentei varias coisas e não consegui resolver meu problema.Seguinte estou reescrevendo um projeto meu para esse ambiente JSF/Spring, neste meu projeto tenho um Bean “A” que possui a anotação @produces e a anotação do Qualificador em uma propriedade “X” para que eu posssa utilizar essa propriedade “X” no Bean “B”. Mas com spring não consigo reproduzir esse cenario, os Beans do novo projeto estão como @component e consegui migrar todas as outras regras, mas essa não estou conseguindo.

  19. Alexandre Afonso Autor do post-

    Beleza Anderson?

    Acho pode criar uma classe de configuração (anotada com @Configuration) e colocar o seu método produtor lá dentro. Ao invés de anotar o produtor com @Produces, anote com @Bean e sobre o qualificador, pode usar a anotação @Qualifier. Seria isso?

    Abraço!

  20. Anderson Dias -

    Então já fiz dessa forma mas acontece que no projeto antigo anotando essa propriedade (que é uma entidade) com @Produces eu capturo ela depois que ela já foi manipulada pelo controller e passo para o outro controller, e com essa configuração do spring de usar método produtor não consigo este mesmo cenário. não sei se posso estar fazendo algo errado,mas vou tentar de novo, obrigado.

  21. Antonio Lazaro -

    Alexandre,

    parabéns pelo post. Bastante esclarecedor e didático, porém fiquei com uma dúvida. Um Bean JSF nativo, ao ser invocado com VIewScoped o método que está anotado com @PostConstruct só é invocado uma vez. Nesse exemplo, o método está sendo chamado N vezes que a página seja processada ou aconteça alguma requisição. Você tem alguma sugestão do que pode ser?

  22. Alexandre Afonso Autor do post-

    Antônio, eu tive curiosidade em analisar esse caso, fiz um teste aqui, mas o @PostConstruct foi chamado apenas uma vez a cada requisição para página. Inclusive, ele não é disparado nem nos postbacks posteriores a requisição para a página. Por ora, não sei o que pode ter acontecido no seu caso.

    Abraço!

  23. Marcelo rockenbach -

    Ola Alexandre. estou tentando fazer a adaptação que você mostrou aqui, mas quando na pagina XHTML estou tendo um erro:
    ADVERTÊNCIA: /paginas/geradanfe/upload.xhtml @27,72 fileUploadListener=”#{GeraDanfeController.uploadArquivo}”: Target Unreachable, identifier ‘GeraDanfeController’ resolved to null
    ” Parece que ele não esta achando o controller. Tem alguma dica?

    1. Alexandre Afonso Autor do post-

      Olá Marcelo,

      Acredito que possa ser o nome do seu managed bean usado na EL. Ao invés de “GeraDanfeController” use “geraDanfeController” (com “g” minúsculo).

      Abraço!

  24. Anderson B. -

    Ola Alexandre, estou precisando casar Spring Boot com JSF/Primefaces, e gostaria de pedir, se vc tem um exemplo dessa integração em GitHub ou outro repositório por gentileza?

    Desde já, agradeço pelo retorno!

    1. Alexandre Afonso Autor do post-

      Olá Anderson!

      Eu já cheguei a fazer essa integração, mas se fosse fazer ela novamente hoje, usaria esse projeto aqui http://joinfaces.org/

      Abraço!

Deixe um comentário