SeamFramework.orgCommunity Documentation

Capitolo 27. Integrazione con il framework Spring

27.1. Iniezione dei componenti Seam nei bean Spring
27.2. Iniettare i bean Spring nei componenti Seam
27.3. Inserire un bean Spring in un componente Seam
27.4. Bean Spring con scope di Seam
27.5. Uso di Spring PlatformTransactionManagement
27.6. Uso del contesto di persistenza gestito da Seam in Spring
27.7. Uso di una sessione Hibernate gestita da Seam in Spring
27.8. Contesto Applicazione di Spring come componente Seam
27.9. Uso di TaskExecutor di Spring per @Asynchronous

L'integrazione con Spring (parte del modulo IoC di Seam) consente una facile migrazione a Seam dei progetti basati su Spring e consente alla applicazioni Spring di sfruttare le funzionalità chiave di Seam come le conversazioni e la sofisticata gestione del contesto di persistenza.

Nota! Il codice di integrazione di Spring è incluso nella libreria jboss-seam-ioc. Questa dipendenza è richiesta per tutte le tecniche di integrazione seam-spring coperte in questo capitolo.

Il supporto di Seam per Spring fornisce la possibilità di:

L'iniezione di istanze di componenti Seam nei bean Spring viene compiuto usando l'handler di namespace <seam:instance/>. Per abilitare questo handler occorre aggiungere il namespace di Seam al file di definizione dei bean Spring:


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:seam="http://jboss.com/products/seam/spring-seam"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                        http://jboss.com/products/seam/spring-seam
                        http://jboss.com/products/seam/spring-seam-2.1.xsd"
>

Ora ciascuno componente di Seam può essere iniettato in un bean Spring:


<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">
    <property name="someProperty">
        <seam:instance name="someComponent"/>
    </property>
</bean
>

Un'espressione EL può essere usata al posto del nome del componente:


<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">
    <property name="someProperty">
        <seam:instance name="#{someExpression}"/>
    </property>
</bean
>

Le istanze dei componenti Seam possono essere disponibili anche per l'iniezione nei bean Spring tramite l'id del bean.


<seam:instance name="someComponent" id="someSeamComponentInstance"/>

<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">
    <property name="someProperty" ref="someSeamComponentInstance">
</bean>

Adesso alcuni caveat!

Seam è stato progettato da principio per supportare un modello a componenti stateful con contesti multipli. Spring no. A differenza della bijection di Seam, l'iniezionedi Spring non avviene al momento dell'invocazione del metodo. Invece avviene solo quando viene istanziato il bean Spring. Quindi quando viene istanziato il bean l'istanza disponibile sarà la stessa istanza che il bean usa per l'intera vita del bean. Per esempio, se un'istanza di un componente con scope CONVERSATION viene direttamente iniettata in un bean singleton di Spring, tale singleton manterrà un riferimento alla stessa istanza anche quando la conversazioneè terminata! Questo problema viene chiamato impedenza di scope. La bijection di Seam assicura che l'impedenza di scope sia naturalmente mantenuta quando un'invocazione attraversa il sistema. In Spring occorre iniettare un proxy del componente Seam e risolvere il riferimento quando il proxy viene invocato.

Il tag <seam:instance/> consente di creare automaticamente il componente proxy del componente Seam.


<seam:instance id="seamManagedEM" name="someManagedEMComponent" proxy="true"/>

<bean id="someSpringBean" class="SomeSpringBeanClass">
    <property name="entityManager" ref="seamManagedEM">
</bean
>

Quest'esempio mostra una modalità d'uso del contesto di persistenza gestito da Seam da un bean di Spring. (Per vedere un modo più robusto di usare i contesti di persistenza gestiti da Seam in sostituzione del filtro OpenEntityManagerInView di Spring si veda la sezione Uso di un contesto di persistenza gestito da Seam in Spring)

E' ancora più facile iniettare i bean Spring nelle istanze dei componenti Seam. In verità ci sono due approcci:

Si discuterà la seconda opzione nella prossima sezione. L'approccio più facile è accedere ai bean Spring via EL.

DelegatingVariableResolver di Spring è un punto di integrazione che Spring fornisce per integrarsi con JSF. Questo VariableResolver rende disponibili in EL tutti i bean di Spring tramite il loro id bean. Occorrerà aggiungere DelegatingVariableResolver a faces-config.xml:


<application>
    <variable-resolver>
        org.springframework.web.jsf.DelegatingVariableResolver
    </variable-resolver>
</application
>

Poi si possono iniettare i bean Spring usando @In:

@In("#{bookingService}")

private BookingService bookingService;

L'uso dei bean Spring in EL non è limitato all'iniezione. I bean Spring possono essere usati ovunque sono usate le espressioni EL in Seam: definizioni di processo e pageflow, asserzioni nella working memory, ecc.

L'handler namespace <seam:component/> può essere usato per far diventare un bean Spring un componente Seam. Si collochi il tag <seam:component/> dentro la dichiarazione del bean che si vuole rendere componente Seam:


<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">
    <seam:component/>
</bean
>

Di default, <seam:component/> creerà un componente Seam STATELESS con classe e nome forniti nella definizione del bean. Occasionalmente, come ad esempio quando viene usato un FactoryBean, la classe del bean di Spring potrebbe non essere la classe che appare nella definizione del bean. In tali casi la class deve essere specificata esplicitamente. Un nome di un componente Seam può essere specificato esplicitamente nei casi in cui c'è un potenziale conflitto di nomi.

L'attributo scope di <seam:component/> può essere impiegato se si vuole che un bean Spring venga gestito in un particolare scope di Seam. Il bean di Spring deve avere scope prototype se lo scope di Seam specificato è diverso da STATELESS. I bean di Spring preesistenti hanno di solito un carattere fondamentalmente stateless, quindi quest'attributo non è di solito necessario.

Il pacchetto di integrazione di Seam consente di usare i contesti Seam come scope personalizzati di Spring 2.0. Questo permette di dichiarare un bean Spring in un qualsiasi contesto di Seam. Comunque, si noti nuovamente che il modello a componenti di Spring non è stato progettato per supportare la modalità stateful, quindi si usi questa funzionalità con molta attenzione. In particolare il clustering dei bean di Spring con scope sessione o conversazione è molto problematico e deve esserci molta attenzione quando si inietta un bean o un componente da uno scope più ampio in un bean con scope più piccolo.

Specificando una sola volta <seam:configure-scopes/> nella configurazione di un bean factory di Bean, tutti gli scope di Seam saranno disponibili ai bean di Spring come scope personalizzati. Per associare un bean di Spring con un particolare scope di Seam, occorre specificare lo scope di Seam nell'attributo scope della definizione del bean.


<!-- Only needs to be specified once per bean factory-->
<seam:configure-scopes/>

...

<bean id="someSpringBean" class="SomeSpringBeanClass" scope="seam.CONVERSATION"/>

Il prefisso del nome dello scope può essere cambiato specificando l'attributo prefix nella definizione di configure-scopes. (Il prefisso di default è seam.)

Di default un'istanza di un componente Spring registrato in questo modo non viene automaticamente creato quando viene referenziato usando @In. Per avere un'istanza auto-creata si deve o specificare @In(create=true) nel punto di iniezione per identificare un bean specifico da autocreare oppure si può usare l'attributo default-auto-create di configure-scopes per rendere auto-creati tutti i bean di Spring che usano uno scope di Seam.

I bean di Spring con scope Seam definiti in questo modo possono essere iniettati in altri bean di Spring senza l'uso di <seam:instance/>. Comunque si presti molta attenzione affinché venga mantenuta la impedenza di scope. L'approccio normale usato in Spring è quello di specificare <aop:scoped-proxy/> nella definizione del bean. Comunque i bean di Spring con scope Seam non compatibili con <aop:scoped-proxy/>. Quindi se occorre iniettare un bean di Spring con scope Seam in un singleton, deve essere usato <seam:instance/>:


<bean id="someSpringBean" class="SomeSpringBeanClass" scope="seam.CONVERSATION"/>

...

<bean id="someSingleton">
    <property name="someSeamScopedSpringBean">
        <seam:instance name="someSpringBean" proxy="true"/>
    </property>
</bean
>

Spring fornisce un'astrazione estensibile della gestione delle transazioni con supporto a molte API per le transazioni (JPA, Hibernate, JDO e JTA). Spring fornisce anche una stretta integrazione con molti TransactionManagers degli application server quali Websphere e Weblogic. La gestione delle transazioni di Spring espone il supporto a funzionalità avanzate quali transazioni innestate e supporto alle regole di propagazione delle transazioni Java EE come REQUIRES_NEW e NOT_SUPPORTED. Per maggiori informazioni guardare here.

Per configurare Seam ad usare le transazioni di Spring abilitare il componente SpringTransaction in queso modo:


<spring:spring-transaction platform-transaction-manager="#{transactionManager}"/>

Il componente spring:spring-transaction utilizzerà le capacità di sincronizzazione delle transazioni di Spring per le callback di sincronizzazione.

Una delle caratteristiche più potenti di Seam è lo scope conversazione e la possibilità di avere un EntityManager aperto per l'intera vita di una conversazione. Questo elimina i problemi associati al detach e re-attach degli entity così come vengono mitigate le apparizioni della temuta LazyInitializationException. Spring non fornisce un modo per gestire un contesto di persistenza oltre lo scope di una singola richiesta web (OpenEntityManagerInViewFilter). Quindi sarebbe bello se gli sviluppatori di Spring potessero avere accesso al contesto di persistenza gestito da Seam usando tutti gli stessi tool che Spring fornisce per l'integrazione con JPA (es. PersistenceAnnotationBeanPostProcessor, JpaTemplate, ecc.)

Seam fornisce un modo per far accedere Spring ad un contesto di persistenza gestito da Seam con i tool JPA forniti da Spring che portano nelle applicazioni Seam le capacità del contesto di persistenza con scope conversazione.

Questo lavoro di integrazione fornisce la seguente funzionalità:

Il modello di propagazione del contesto di persistenza di Spring consente solo un EntityManager aperto per EntityManagerFactory, quindi l'integrazione con Seam funziona facendo il wrapping dell'EntityManagerFactory attorno al contesto di persistenza gestito da Seam.


<bean id="seamEntityManagerFactory" class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">
           <property name="persistenceContextName" value="entityManager"/>
</bean
>

dove 'persistenceContextName' è il nome del componente con contesto di persistenza gestito da Seam. Di default questo EntityManagerFactory ha un unitName uguale al nome del componente Seam o in questo caso 'entityManager'. Se si vuole fornire un unitName diverso, si può fornire un persistenceUnitName così:


<bean id="seamEntityManagerFactory" class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">
           <property name="persistenceContextName" value="entityManager"/>
        <property name="persistenceUnitName" value="bookingDatabase:extended"/>
</bean
>

EntityManagerFactory può essere usata in un qualsiasi tool fornito da Spring. Per esempio, l'uso di PersistenceAnnotationBeanPostProcessor di Spring è lo stesso di prima.


<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

Se si definisce in Spring un EntityManagerFactory ma si vuole usare un contesto di persistenza gestito da Seam, si può dire al PersistenceAnnotationBeanPostProcessor quale persistenctUnitName si desidera usare come default specificando la proprietà defaultPersistenceUnitName.

applicationContext.xml potrebbe apparire come:


<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="bookingDatabase"/>
</bean>
<bean id="seamEntityManagerFactory" class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">
           <property name="persistenceContextName" value="entityManager"/>
        <property name="persistenceUnitName" value="bookingDatabase:extended"/>
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
        <property name="defaultPersistenceUnitName" value="bookingDatabase:extended"/>
</bean
>

component.xml potrebbe così apparire:


<persistence:managed-persistence-context name="entityManager"
        auto-create="true" entity-manager-factory="#{entityManagerFactory}"/>

JpaTemplate e JpaDaoSupport sono configurati allo stesso modo per un contesto di persistenza gestito da Seam.


<bean id="bookingService" class="org.jboss.seam.example.spring.BookingService">
        <property name="entityManagerFactory" ref="seamEntityManagerFactory"/>
</bean
>

L'integrazione di Spring con Seam fornisce anche il supporto al completo accesso alla sessione Hibernate gestita da Seam usando i tool di Spring. Quest'integrazione è molto simile all'integrazione JPA.

Come per l'integrazione JPA di Spring, il modello di propagazione di Spring consente di avere aperto solo un EntityManager per EntityManagerFactory per transazione disponibile ai tool di Spring. Quindi , l'integrazione della Sessione di Seam funziona solo con il wrap di un proxy SessionFactory attorno al contesto sessione di Hibernate gestito da Seam.


<bean id="seamSessionFactory" class="org.jboss.seam.ioc.spring.SeamManagedSessionFactoryBean">
        <property name="sessionName" value="hibernateSession"/>
</bean
>

dove 'sessionName' è il nome del componente persistence:managed-hibernate-session. Questo SessionFactory può essere allora usato in un qualsiasi tool fornito da Spring. L'integrazione fornisce anche supporto alle chiamare a SessionFactory.getCurrentInstance() finché si chiama getCurrentInstance() su SeamManagedSessionFactory.

Sebbene sia possibile usare il ContextLoaderListener di Spring per avviare l'ApplicationContext di Spring della propria applicazione, ci sono un paio di limitazioni.

Per superare queste due limitazioni, l'integrazione di Spring include un componente di Seam che avvia un ApplicationContext di Spring. Per usare questo componente di Seam si collochi la definizione <spring:context-loader/> in components.xml. Si specifichi la posizione del file col contesto di Spring nell'attributo config-locations. Se sono presenti più di un file si possono posizionare nell'elemento <spring:config-locations/> seguendo le practice multi-value di components.xml.


<components xmlns="http://jboss.com/products/seam/components"
            xmlns:spring="http://jboss.com/products/seam/spring"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://jboss.com/products/seam/components
                                http://jboss.com/products/seam/components-2.1.xsd
                                http://jboss.com/products/seam/spring
                                http://jboss.com/products/seam/spring-2.1.xsd">

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

</components
>

Spring fornisce un'astrazione per eseguire codice in modo asincrono chiamata TaskExecutor. L'integrazione con Seam consente di usare un TaskExecutor di Spring per eseguire chiamate immediate di metodi @Asynchronous. Per abilitare questa funzionalità si installi SpringTaskExecutorDispatchor e si fornisca un bean di Spring definito taskExecutor in questo modo:


<spring:task-executor-dispatcher task-executor="#{springThreadPoolTaskExecutor}"/>

Poiché un TaskExecutor di Spring non supporta lo scheduling di eventi asincroni, può essere fornito un Dispatcher di Seam per gestire gli eventi asincroni schedulati in questo modo:


<!-- Install a ThreadPoolDispatcher to handle scheduled asynchronous event -->
<core:thread-pool-dispatcher name="threadPoolDispatcher"/>

<!-- Install the SpringDispatcher as default -->
<spring:task-executor-dispatcher task-executor="#{springThreadPoolTaskExecutor}" schedule-dispatcher="#{threadPoolDispatcher}"/>