Spring com Seam, Parte 1: Contruindo um Componente Híbrido Spring-Seam Etapa 03
Finalizando a parte do artigo sobre spring com seam.
Spring com Seam, Parte 1: Contruindo um
Componente Híbrido Spring-Seam Etapa 03
Tratando com proxies AOP e beans cobertos
A definição do bean tournamentManager acima é simplificado ao extremo porque ele não tem capacidades transacionais. Spring se orgulha em prover serviços declarativos, tais como transações, que são completamente transparentes para o bean na qual elas estão registradas. No entanto, é importante entender o mecanismo pelo qual esses serviços são aplicados porque o mecanismo de proxy do Spring tem o potencial de lançar um golpe na integração Spring-Seam. Nós veremos um exemplo deste problema e aprenderemos como contorná-lo. Na Listagem 9, vamos iniciar adicionando alguns conselhos de transações para este bean usando configuração AOP (Aspected Oriented Programming) do Spring. A definição do gerente de transação é também apresentada nesta listagem.
Listagem 9. Adicionando conselho de transação com AOP
Esta configuração essencialmente diz a implementação do PlatformTransactionManager, que neste caso é JpaTransactionManager, para aplicar transações ao longo de todos os métodos das classes que implementam a interface TournamentManager. Qualquer método comecando com get usa uma transação somente leitura (sem flush - descarrego), considerando que as transações restantes forçam um flush (descarrego) do contexto de persistência antes do commit. Porém, isso é apenas um comportamento padrão do Spring e não afeta como o componente Seam é derivado do bean de destino.
A configuração que afeta como o componente Seam é criado é o atributo proxy-target-class no elemento <aop:config>, destacado em negrito na Listagem 9. este ponto é bastante importante: Seam não pode adotar JDK proxied Spring beans. Seam pode apenar lidar com classes instrumentdas Cglib e Javassist.
Hã? Se você é novo em proxy dinâmico e manipulação de bytecode, isso pode soar como grego pra você. Basicamente, como parte do mecanismo de AOP, Spring envolve seu bean Spring em uma camada que pode interceptor chamadas de métodos e adicionar funcionalidade antes e depois de cada invocação. Esta técnica é exatamente o que Seam faz para adicionar capacidades tais como bijeção para seus componentes. No entanto, Seam realiza este comportamento usando melhorias de bytecode em tempo de execução provido pela biblioteca Javassist. Spring, por outro lado, usa proxies dinâmicos JDK por padrão. A infra-estrutura de proxy do JDK é apenas capaz de criação de proxies para interfaces, considerando as bibliotecas de tratamento de bytecode, incluindo Javassist e Cglib, podendo trabalhar diretamente nas classes.
Basta dizer, Seam não pode trabalhar com o bean Spring se ele usa proxy dinâmico JDK. Essa questão nao surge quando acessando um bean Spring a partir do RLE porque Seam somente o chama nos métodos. Aqui, o bean Spring bean está sendo convertido em um componente Seam, então Seam é envolvido no inferno do processo de criação de bean e necessita de nível extra de visibilidade.
A situação é esta: Seam não pode lidar com proxies JDK. Spring não pode tratar beans usando Javassist. Para chegar a um consenso, Spring pode usar tratamento de bytecode em tempo de execução provido pelo Cglib, que é suficiente para fazer o Seam feliz. A troca para este provedor de proxy alternativo acontece por bean. No caso do elemento <aop:confg>, esta traca é feita usando o atributo proxy-target-class. Apesar do nome horrível para este atributo, um valor true permite proxies Cglib tanto como proxies JDK dinâmicos. Se você não faz esta troca, você receberá a seguinte exceção quando você tentar transformar um bean Spring no qual já foi feito o proxy em um componente híbrido Spring-Seam:
Caused by: java.lang.RuntimeException: Seam cannot wrap JDK proxied IoC beans.
Please use CGLib or Javassist proxying instead at org.jboss.seam.ioc.ProxyUtils.enhance(ProxyUtils.java:52)
O que fazer se pointcuts simplesmente não são seu copo de chá? Você pode optar, ao invés, em usar o bean TransactionProxyFactoryBean do Spring para envolver seu objeto da camada de serviço em transações. Este bean possui uma troca similar para sua configuração de proxy, mas esta não é o único aspecto que irá encontrar. Você agora tem um novo problema. Quando você usa um bean proxy factory, o nome da classe do objeto de destino não está na definição <bean>. A Listagem 10 exibe tal situação.
Listagem 10. Usando um bean proxy factory para evitar pointcuts
Esta definição de bean possui as mesmas propriedades de transação como a apresentada na Listagem 9, que usou expressão pointcut de AOP. Ela também configure a propriedade proxyTargetClass para true para permitir proxy Cglib. No entanto, note que o elemento <seam:component> está agora usando o atributo class para especificar o nome da classe que implementa a interface de camada de serviço. Esta classe é também fornecida na propriedade target da definição do bean. Por que especificar isso em ambos os lugares?
A classe de implementacao debe ser declarada explicitamente para que o Seam conheça qual o tipo de objeto a fábrica de bean do Spring produz. Esta informação está enterrada profundamente dentro da definição <bean>, onde o Seam não está apto a encontrá-lo. Sem esta informação, Seam não pode realizar operações que requerem conhecimento do tipo do componente híbrido, tais como injeção dele na propriedade de outro componente. O que é pior é que sem este detalhe, Seam na verdade pega o tipo errado, dado que o atributo class na definição <bean> é consultado para determinar o tipo do componente. Neste exemplo, Seam assume que a classe do componente tournamentManager component é TransactionFactoryProxyBean.
Este problema também acontece quando usando a funcionalidade de hierarquia de definição de bean do Spring (o atributo pai na definição <bean>). Quando a tag <seam:component> é usada na definição de um bean filho, você mais uma vez tem que educar o Seam sobre qual tipo esperar. Esta incompatibilidade com a hierarquia do bean provavelmente sera resolvida em breve, pois todas as informações que o Seam precisa está na hierarquia da definição do bean – é apenas uma forma do Seam procurar por ela. No entanto, o ponto que resta é que se existe pelo menos um caso onde o Seam não pode determinar o tipo, ou pega o tipo errado, você pode suprir isso usando o atributo da classe.
Apesar do atributo class ser requerido para se obter o componente híbrido registrado de forma correta, Seam não tem problema em trabalhar com um factory bean do Spring factory uma vez que o componente híbrido é configurado. Quando você injeta o componente tournamentManager em uma propriedade TournamentManager em um outro componente Seam, Seam corretamente desenvolve o factory bean para obter o objeto de destino no qual foi feito proxy, em oposição à tentativa de injetar o factory bean por si só. O que você também pode achar interessante é que o Seam pode aplicar corretamente as propriedades de configuração do componente para o bean de destino.
Isso nos deixa iniciado na criação de componente Seam com a tag <seam:component>. Vamos dar mais ênfase à significância de um bean Spring se tornando um componente Seam e aprender como ajustar o comportamento do componente Seam que ele gera.
Tornando-se um Componente Seam
Como mencionado anteriormente, quando uma tag <seam:component> é detectada dentro de um elemento <bean>, a implementação da classe NamespaceHandler do Seam envolve o bean Spring em uma instância de SpringComponent e registra ele com o container Seam, tornando o bean Spring um componente Seam complemente encorpado. Isso significa que quando um método é invocado bean Spring, todos os interceptores normais do Seam são aplicados. Pense sobre isso por um momento. Você pode agora usar todas as bijeções do Seam em seu bean Spring! Vamos assumir que um bean Spring chamada tournamentDao, responsável por realizar busca em um banco de dados, é também configurado como um componente híbrido Spring-Seam como explicado acima. A Listagem 11 mostra como este objeto de acesso aos dados (DAO) pode ser injetado no objeto de negócio do gerente de torneio usando a anotação @In do Seam, uma alternativa para usar o mecanismo de injecao de dependência do Spring. Adicionalmente, a anotação @Logger do Seam é usada para instanciar e injetar um logger.
Listagem 11. Usando bijação em um bean Spring
Agora nós precisamos fornecer o argumento do critério d busca para o método finder e capturar o resultado. Isso é feito através do uso de um factory definido no descritor do componente Seam (components.xml). O método finder é referenciado como uma expressão de valor e o valor de retorno é direcionado a uma variável de contexto do Seam usando:
Note que a expressão de valor toma vantagem da Linguagem de Expressão JBoss EL, que permite que um parâmetro seja passado para um método, neste caso o critério de busca conversation-scoped. Agora é possível interagir sobre os resultados das buscas na interface do usuário referenciando a expressão de valor #. Você pode também tirar vantagem de anotações adicionais do Seam, tais como @Factory, @Restrict, @RaiseEvent, @Observer e @Transactional, para citar algumas.
Se você não deseja poluir muito seu bean Spring com interceptores Seam, preferindo deixar seu bean Spring intactos, você pode disabilitar os interceptores Seam atravésda configuração do atributo intercept da tag <seam:component> para false. Desabilitar os interceptores pode melhorar desempenho, especialmente se você não for usar qualquer uma das funcionalidades que eles provêm de forma alguma. A mudança para a definição é exibida em negrito na Listagem 12.
Listagem 12. Disabilitando interceptores Seam
Com ou sem os interceptores habilitados, você pode ainda usar o mecanismo de configuração de componente do Seam para configurar os valores iniciais das propriedades no componente híbrido Spring-Seam. Isso inclui injeção de log para o acessor de campo ou propriedade decorado com @Logger. No entanto, customizar propriedades no Seam deve ser provavelmente usada moderadamente (talvez apenas para sobreposições específicas do ambiente) visto que ela pode confundir outros desenvolvedores – ou talvez até você mesmo – quando tentando obter uma imagem completa da definição do componente. Em vez disso, a recomendação é usar o mecanismo de configuração de propriedade do Spring.
Componentes híbridos Spring-Seam, por padrão, são configurados ara serem auto-criados. No entanto, neste caso, o termo é um pouco mal entendido. Isso porque o bean Spring pode já ser inicializado no momento que a variável de contexto é requisitada pela primeira vez, talvez porque ele foi criado quando o container Spring inicializou. Mas Seam ainda considera a variável de contexto não iniciada neste momento, pois o Seam não consultou ainda o container Spring para determinar se o the foi criado. Assim, o atributo auto-create na tag <seam:component> realmente significa "tentative de resolver o bean Spring sem fizer para fazer isso." Se este não é o comportamento desejado, você pode desabilitá-lo mudando o valor do atributo auto-create da tag <seam:component> para false, como na Listagem 13.
Listagem 13. Disabling automatic lookup
A configuração auto-create é especialmente relevante considerando que a maioria dos componentes híbridos Spring-Seam são stateless, resultando em uma variável de contexto que nunca é permanentemente inicializada. Assim, você provavelmente irá querer que o valor do atributo auto-create seja true. Se seu valor for false, então ele necessita o uso da flag create na anotação @In quando referenciando a variável de contexto correspondente. Expressões de valores de Expressões Lógicas que referenciam as variáveis de contexto não são afetadas, pois eles realizam o lookup implicitamente.
Sobre a variável de contexto sendo requisitada, se a flag create é true e se o bean Spring bean existe, ele irá retornar ao Seam. Se a flag create é true e o bean Spring não existe, o container Spring irá tentar criá-lo, e então retornar a instância ao Seam. Se a flag create é false, nenhuma tentativa será feita para comunicar com o container Spring.
O melhor dos dois mundos
Você pode agora usar beans Spring como você precisa em sua aplicação Seam. O Framework Spring é preenchido com componentes Seam potenciais e funcionalidades convenientes. Não faria qualquer sentido dizer o contrário. Suplementando Seam com Spring irá provavelmente provar ser muito valioso para o sucesso de sua aplicação. Por que escolher entre apenas um?
De fato, Seam pode normalmente tornar os beans Spring melhores ensinando-os uma ou duas coisas sobre manter o estado de aplicação -- e você irá aprender como este trabalha no próximo artigo desta série. Até então, você pode tentar construir seu próprio componente híbrido Spring-Seam para obter uma sensação mais precisa de como os dois frameworks se complementam.
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo