Spring com Seam, Parte 1: Contruindo um Componente Híbrido Spring-Seam – Etapa 01

Qual é melhor, Seam ou Spring? Aprenda porque você não precisa escolher

 

Dan Allen

É um consultor de software independente, autor e defensor de software livre. Graduado em Ciências e Engenharia de Materiais pela Universidade de Cornell em 2000. Ele descobriu a combinação de Linux e a plataforma Java EE para ser o alicerce para construir sua carreira profissional. Dan é autor do livro Seam in Action, publicado pela Manning Publications, é também membro do comitê do projeto Seam, um participante ativo na comunidade do Seam e um Java blogger.

JBoss Seam é um intrigante framework que unifica JSF, EJB 3, JPA e JAAS sobre um modelo baseado em componente acessível. Mas ao adotar Seam não significa que você tem que perder as vantagens de outros frameworks, tais como Spring. Nesta série, o autor Dan Allen mostra aos leitores como – e por que -- Seam e Spring podem trabalhar juntos. Nesta primeira parte, aprenda como construir componentes híbridos Seam-Spring que se beneficiam das funcionalidades providas por ambos.

Se você olhar dentro da bolsa de golf de qualquer golfista não-profissional, você irá provavelmente encontrar equipamentos de uma grande variedade de marcas. Um golfista pode achar que um taco da Nike é melhor para tacadas próximas do buraco, que um TaylorMade ajuda a não jogar a bola próxima de florestas e que metais Cleveland são mais apropriados em outras situações. Talvez isso seja psicológico, mas a verdade por trás dessa diversidade é que as empresas que desenvolvem esses equipamentos se especializam em diferentes produtos. Uma empresa pode não ter a mesma paixão para projetar ótimos tacos para bolas próximas ao buraco que eles têm para projetar tacos para longa distância. Para um golfista, uma marca nem sempre é suficiente. O mesmo pode ser dito para uma caixa de ferramenta de desenvolvedores.

Para fomentar tais diversidades, o JBoss Seam provê um módulo para integração do Framework Spring em uma aplicação Seam. Seam unifica JavaServer Faces, Enterprise JavaBeans 3, JPA (Java Persistence API), Serviço de Autenticação e Autorização Java sobre um modelo baseado em componente acessível, possibilitando que você crie aplicações com tremenda agilidade. Ainda, Spring fornece muitas funcionalidades que você não encontrará no Seam. É provável que essas funcionalidades evolvam declarações de programação orientada a aspectos (AOP), classes de template de framework e injeções de recursos de container emuladas, para mencionar apenas algumas. Essa disparidade não é necessariamente uma deficiência. Você não precisa excluir Seam da arquitetura de uma aplicação apenas porque está faltando uma funcionalidade que o Spring possui. Ao invés disso, você devia reconhecer que essas diferenças são traduzidas em mais ferramentas disponíveis para ser aplicada, assumindo que você optou por usar os dois frameworks.

Neste artigo você aprenderá como se beneficiar do container Spring a partir do Seam e vice-versa. Este artigo é projetado para desenvolvedores que estão usando Seam ou JSF e gostariam de aproveitar as vantagens das capacidades únicas do Spring, ou aqueles que estão contemplando uma troca para Seam como uma plataforma primária, mas não deseja desistir do seu investimento feito com o Spring. O texto é escrito com a suposição de que o leitor possua um entendimento sobre os conceitos básicos de Seam e Spring, com referência limitada ao JSF. No entanto, mesmo sem esse embasamento, o leitor é capaz de apreciar este artigo. A motivação para escrever este artigo é deixar a comunidade conhecer que o Seam e Spring não são tecnologias mutuamente exclusivas e que é possível que sua combinação ofereça mais valor que se o desenvolvimento fosse feito usando apenas um deles.

Neste primeiro artigo da série, teremos uma introdução ao componente híbrido Spring-Seam, que é um objeto gerenciado que se beneficia a partir da funcionalidade provida por ambos os containers Seam e Spring.

Seam e Spring trabalhando juntos

Uma forma de integrar beans Spring com uma aplicação Seam é fazendo o container Seam se comunicar com Spring indiretamente por meio do uso de um resolvedor de linguagem de expressão (RLE) customizado. Esta abordagem é comumente usada para integrar Spring com JSF, que dá ao Seam acesso ao beans Spring. Quando o RLE processa uma expressão (ex: #{tournamentManager.tournaments}), ele tenta resolver o objeto base procurando ambos os containers Spring e Seam para um componente com um nome que bate com a raiz da expressão (ex: tournamentManager), retornando uma falha ao JSF-managed bean container se a busca retorna vazio. Enquanto esta técnica pode ser o bastante para casos de uso simples, existem vários ganhos por estabelecer uma integração entre os dois containers. O objetivo é ter os componentes reconhecendo a presença um do outro, mais que ter o RLE mediando as interações entre eles.

Para permitir que o cross-breeding ocorra, é essencial ter os dois containers inicializados simultaneamente, e não de forma isolada. Caso contrário, você pode chegar a uma situação onde cada container tem uma dependência sobre componente de inicialização a partir de outro, prevenindo que algum container esteja pronto para carregar o componente primeiro que o outro. Felizmente, Seam pode facilitar uma inicialização combinada, cuidando de resolver qualquer pré-requisito circular.

Inicializando Spring e Seam simultaneamente

Seam e Spring provêm listeners de contexto de servlet que são usados para dar boot aos respectivos containers. Se você registra ambos os listeners na mesma aplicação Web, então os containers associados são carregados baseados na ordem relativa dos listeners no arquivo descritor web.xml. Apesar de proteger a idéia de que a ordem de carregamento sozinha pode garantir uma inicialização com sucesso, é melhor dar a um container a responsabilidade de inicializar o outro. Seam sai na frente e oferece o componente ContextLoader (<spring:context-loader>) para realizar esta tarefa. Este componente interno do Seam realiza o mesmo trabalho que o ContextLoaderListener do Spring, então o container Spring não reconheceria que estaria sendo inicializado por um mecanismo diferente. Se você for usar Spring e Seam juntos em uma aplicação, é fortemente recomendado que você interfira nesta configuração de boot.

O componente ContextLoader permite que um ou mais arquivos de localização de configuração do Spring sejam especificados na propriedade config-locations da tag <spring:context-loader>, definida no descritor do componente do Seam. A propriedade config-locations é um vetor de String e pode ser configurado em uma forma típica para uma propriedade de componente multi-valorado. Imitando o comportamento do ContextLoaderListener, se nenhuma localização de configuração é especificada na tag <spring:context-loader>, assume-se que a configuração do Spring está apresentada no caminho de recurso /WEB-INF/applicationContext.xml. Com o Seam assumindo o controle da inicialização do Spring, o parâmetro de contexto do servlet contextConfigLocation no descritor web.xml não fica tão longo.

Aqui estão dois exemplos da configuração do ContextLoader no descritor do componente do Seam, /WEB-INF/components.xml (ou /META-INF/components.xml). A Listagem 1 usa um simples arquivo de configuração do Spring em uma localização diferente do padrão.

Listagem 1. Carregando um arquivo de configuração do Spring

<spring:context-loader config-locations="/WEB-INF/spring-beans.xml"/>

A Listagem 2 divide arquivos de configuração do Spring em camadas e posiciona eles no CLASSPATH em vez do diretório de configuração da Web.

Listagem 2. Carregando múltiplos arquivos de configuração do Spring

<spring:context-loader

  config-locations="classpath:spring-beans-persistence.xml

classpath:spring-beans-service.xml"/>

Como a Listagem 3 ilustra, você pode também quebrar o valor da propriedade em sub-elementos.

Listing 3. Múltiplos arquivos de configuração do Spring, definidos explicitamente em itens

<spring:context-loader>

  <spring:config-locations>

    <value>classpath:spring-beans-persistence.xml</value>

    <value>classpath:spring-beans-service.xml</value>

  </spring:config-locations>

</spring:context-loader>

Para usar esta definição de componente, você deve registrar o namespace XML para o módulo Spring na parte superior do descritor do componente Seam, como mostra a Listagem 4. Note que os namespaces exibidos são diferentes dos namespaces XML usados em seu arquivo de configuração do Spring.

Listagem 4. Registrando o namespace do XML do Spring

<components xmlns="http://jboss.com/products/seam/components"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns:spring="http://jboss.com/products/seam/spring"

  xsi:schemaLocation="

    http://jboss.com/products/seam/components

    http://jboss.com/products/seam/components-2.0.xsd

    http://jboss.com/products/seam/spring
    http://jboss.com/products/seam/spring-2.0.xsd">

  <!-- component declarations -->

</components>

Colocando Seam encarregado de carregar o container Spring também facilita os testes de integração das aplicações Seam que dependem de um beans Spring. Quando a infra-estrutura de teste de integração do Seam carrega o container Seam, ele transitivamente carrega o container Spring. A infra-estrutura de teste de integração do Seam é ativada através da escrita de testes que estendem a classe base SeamTest.

Usar Seam e Spring na mesma aplicação vai além de apenas dividir seus objetos gerenciados entre os dois containers. O que esta integração proporciona a você é a possibilidade de promover beans Spring em componentes Seam no nível de metadados de configuração para formar um componente híbrido Spring-Seam, que iremos aprender na próxima seção. O carregamento simultâneo de dois containers permite este tipo de integração porque o container Seam está ativo e pronto para registrar componentes adicionais no momento em que o arquivo de configuração do Spring é transformado.