Criando e gerenciando objetos de terceiros com Spring e @Bean

Umas das premissas mais importantes quando trabalhamos com algum framework IoC/DI, como Spring ou CDI, é delegar a criação e o gerenciamento dos objetos para seus containers. Isso permite que nosso código seja mais flexível e menos acoplado a lógica de criação de objetos complexos ou de terceiros.

Assim como a anotação @Produces do CDI, o Spring desde sua versão 3.0 nos permite obter o mesmo comportamento através da anotação @Bean. A idéia é a mesma, criar objetos caros, complexos ou de outros frameworks e registrá-los no container para que eles possam ser injetados como dependências noutras classes do sistema.

Escrevi um artigo no blog da TriadWorks sobre o assunto com mais detalhes sobre a anotação @Bean e suas vantagens. Vale a pena a leitura e uso da prática!

Aproveitando os beans do Spring em suas páginas JSF

Esses dias estava tendo uma conversa bem produtiva com um amigo de trabalho, Tarso Bessa, o cara é muito safo em desenvolvimento e sempre me aparece com umas idéias bem bacanas. Nessa conversa ele chegou com uma idéia bem interessante sobre aproveitarmos a integração do Spring com JSF, a idéia se resume em aproveitar o variable resolver do Spring nas páginas JSF, assim poderíamos acessar qualquer bean gerenciado pelo Spring (Service, DAO, Repository, whatever) nas páginas através de JSF EL sem a necessidade de intermediar o acesso à este bean com um managed bean do JSF.

O problema

O problema realmente surgiu enquanto o Tarso implementava um mecanismo de CRUD genérico para nossa aplicação, foi aí que começamos a discutir sobre o problema e em seguida a solução.

Na verdade, no projeto que estamos desenvolvendo nós utilizamos muitas combo-boxes (tr:SelectOneChoice pois utilizamos o Myfaces Trinidad) nas nossas páginas e ter que criar um ou mais métodos nos nossos managed beans somente para popular essas combos com dados trazidos diretamente do banco de dados -sem qualquer lógica-, além de ser uma tafera repetitiva, não era nada cômodo.

No final das contas só queríamos não escrever mais código repetitivo nos nossos managed beans (principalmente os que estendiam o nosso CRUD genérico) simplemente para podermos acessar nas páginas uma lista de objetos trazidos do banco de dados.

A solução

Com a integração do Spring com JSF é possível fazer injeção de dependências (DI) dos beans do Spring dentro dos managed beans da aplicação pelo faces-config.xml, algo como:

[faces-config.xml]

<managed-bean>
    <managed-bean-name>bacalhauBean</managed-bean-name>
    <managed-bean-class>bean.faces.BacalhauBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
        <property-name>someService</property-name>
        <property-class>model.service.SomeService</property-class>
        <value>#{someService}</value>
    </managed-property>
</managed-bean>

Imaginem que nós tenhamos um bean chamado “someService” configurado no spring-config.xml, ok?
Agora observem que o bean gerenciado pelo Spring é resolvido através da EL do JSF, #{someService}, então por que não utilizar-se disso para acessarmos diretamente este bean dentro das páginas? 😀
Será isso que iremos fazer logo abaixo!

Imagine que nossa implementação da interface SomeService seja algo do tipo:

[SomeServiceImpl.java]

public class SomeServiceImpl implements SomerService {

    // outros métodos deste serviço

    public List<Foo> findAll() {
        // executa a pesquisa através de um DAO, ou repositório, whatever..
    }

    public List<Foo> getAll() {
        // faz chamada ao método findAll
        return this.findAll();
    }
}

Assim no código da página nós precisaríamos fazer apenas isto:

[foo.jsp]

<h:selectOneMenu id="fooId" value="#{bacalhauBean.fooId}">
    <t:selectItems value="#{someService.all}" var="o" itemValue="o.id" itemLabel="o.name" />
</h:selectOneMenu>

Observem que estou passando a lista de objetos para o componente através da EL #{someService.all} que reflete o método SomeServiceImpl.getAll() do bean gerenciado pelo Spring, atentem também que estou utilizando o componente t:selectItems do Myfaces Tomahawk para que eu possa iterar uma lista de objetos comuns, pois sem ele seria necessário passar uma lista de SelectItem’s, o que não ficaria legal nas nossas classes de serviços.

O melhor de tudo é que não há a necessidade de utilizar um managed bean para intermediar a lista de dados para o componente, o EL do JSF acha o bean “someService” no contexto do Spring e faz a chamada ao método para você (é necessário que o método comece com “get” para que a EL consiga executar corretamente, por isso criamos um método chamado getAll() na classe do SomeServiceImpl.java).

Graças ao variable resolver do Spring você poderá utilizar o #{someService.all} em qualquer componente que espere uma lista de objetos, como um h:dataTable, h:selectManyMenu ou qualquer outro. Além do mais você poderá obter um objeto qualquer como um entity, ou um mapa, ou uma lista ordenada e/ou filtrada diretamente dos beans gerenciados pelo Spring e utilizá-los nas tuas páginas sem dificuldades.

Uma coincidência é que enquanto o Tarso estava fazendo os testes e implementando a solução ele caiu naquele velho probleminha do selectOneMenu, não exatamente como no cenário que eu havia blogado, mas o problema e a solução foram a mesma, e acabamos resolvendo este problema rapidamente sem perder tempo. É como eu disse no final daquele post, “[…] quem não passou provavelmente irá passar algum dia.” :)

O mais engraçado..

O mais engraçado disso tudo é que já temos tudo isso pronto e muito melhor implementado com o JBoss Seam, o Seam estendeu a EL do JSF e com isso conseguimos acessar qualquer componente no seu contexto através de EL, como DAOs, repositorios, serviços etc, e o melhor é que a sua EL estendida nos fornece a possibilidade de executar métodos parametrizados, ou seja, com ele podemos ter algo como isto:

<h:commandButton action="#{hotelBooking.bookHotel(hotel, user)}" value="Book Hotel"/>

Simplesmente fantástico!
Enfim, para quem não tem o JBoss Seam no projeto, pode ter um pouco do gostinho dele com o Spring.

Concluindo..

Como achei o problema e a solução interessantes eu quis compartilhar com os pouquíssimos leitores deste blog, fica aí a dica senhores.
Valeu Tarso Bessa, valeu gente.