SeamFramework.orgCommunity Documentation

第27章 Spring Framework 統合

27.1. Seam コンポーネントを Spring Bean にインジェクトする
27.2. Spring Bean を Seam コンポーネントにインジェクトする
27.3. Spring Bean を Seam コンポーネントにする
27.4. Seam スコープの Spring Bean
27.5. Spring の PlatformTransactionManagement を使用する
27.6. Spring で Seam 管理の永続コンテキストを使用する
27.7. Spring で Seam 管理の Hibernate セッションを使用する
27.8. Seam コンポーネントとしての Spring Application Context
27.9. @Asynchronous に Spring の TaskExecutor を使用する

Spring 統合モジュールにより Seam への Spring ベースとするプロジェクトの移植が容易になり、 Spring アプリケーションは対話や Seam の高度な永続コンテキスト管理など Seam の主要な機能を利用することができるようになります。

注意! Spring 統合コードは jboss-seam-ioc ライブラリに含まれています。 この依存性は本章に記載されているすべての seam-spring 統合技術に必要となります。

Spring の Seam サポートは次のような機能を提供します。

Seam コンポーネントインスタンスの Spring Bean へのインジェクションは、 <seam:instance/> 名前空間ハンドラを使用して行います。 Seam 名前空間ハンドラを有効にするには、 Seam 名前空間を Spring Bean 定義ファイルに追加しなければなりません。


<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"
>

これで、 Seam コンポーネントはいずれの Spring Bean にもインジェクション可能となりました。


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

コンポーネント名の代わりに EL 式が利用可能です。


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

Seam コンポーネントインスタンスは、 Spring Bean id で Spring Bean へのインジェクションができるようになります。


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

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

警告!

Seamは複数のコンテキストを持つステートフルなコンポーネントモデルに対応することを基本に設計されました。 Spring はそうではありません。 Seam のバイジェクションと異なり、 Spring のインジェクションはメソッド呼び出し時に発生しません。 その代わり、 Spring Bean がインスタンス化されるときだけ、 インジェクションは発生します。 従って、 Bean がインスタンス化されるときに利用可能なインスタンスは、 Beanのライフサイクル全期間で Bean が使用するものと同じインスタンスです。 例えば、 Seam 対話 スコープコンポーネントのインスタンスが 直接、 単一の Spring Bean にインジェクトされると、 その単一の Spring Bean はその対話が終了した後もずっと同じインスタンスに対する参照を保持します。 この問題を スコープインピーダンス (scope impedance) と呼んでいます。 システム全体に呼び出しが流れるように、 Seam バイインジェクションはスコープインピーダンスが自然に維持されるようにします。 Spring では、 Seam コンポーネントのプロキシをインジェクトすることでプロキシが呼ばれた場合に参照を解決する必要があります。

<seam:instance/> タグで自動的に Seam コンポーネントをプロキシできるようになります。


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

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

上記の例では Spring Bean から Seam 管理の永続コンテキストを使用する方法のひとつを示しています。 (Spring OpenEntityManagerInView フィルタの代替として Seam 管理の永続コンテキストを使用するより堅牢な方法については、 Spring で Seam 管理の永続コンテキストを使用する のセクションを参照してください。)

Spring Bean を Seam コンポーネントインスタンスにインジェクトするのはさらに簡単です。 実際、 可能な方法は 2 つあります。

次のセクションでは 2 番目の選択肢について説明します。 もっとも容易な方法は EL を使って Spring Bean にアクセスします。

Spring の DelegatingVariableResolver は Spring を JSF と統合する場合に Spring によって提供される統合ポイントになります。 この VariableResolver はその Bean ID によって EL ですべての Spring Bean を利用可能にさせることができます。 DelegatingVariableResolverfaces-config.xml に追加する必要があります。


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

これを行うと @In を使って Spring Bean をインジェクトできるようになります。

@In("#{bookingService}")

private BookingService bookingService;

EL 式でインジェクションを行うために Spring Bean を使用することに制限はありません。 Seam で EL 式が使用されるところならどこでも Spring Bean を使用することができます。 プロセスとページフロー定義、 ワーキングメモリアサーションなど。

<seam:component/> 名前空間ハンドラを使用すると、 どんな Spring Bean でも Seam コンポーネントにすることができます。 Seam コンポーネントにしたい Bean の宣言内に <seam:component/> タグを配置するだけです。


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

デフォルトでは、 <seam:component/>は Bean 定義で与えられるクラスと名前を付けて STATELESSコンポーネントを生成します。 ときおり、FactoryBean が使用される場合など、 Spring Bean のクラス が Bean 定義に出てくるクラスではないことがあります。 このような場合、 class は明示的に指定されなければなりません。 名前付けに競合の可能性がある場合、 Seam コンポーネント名を明示的に指定しても構いません。

Spring Bean を特定の Seam スコープで管理したい場合、 <seam:component/>scope 属性を使用することができます。 指定される Seam スコープが STATELESS ではない場合、 Spring Bean は prototype にスコープされなければなりません。 既にある Spring Bean は通常ステートレスな特徴を基本的に持っていますので、 この属性は一般的には不要になります。

Seam 統合パッケージにより Seam のコンテキストを Spring 2.0 スタイルのカスタムスコープとして使用することもできるようになります。 これによりいずれの Seam コンテキスト内でもあらゆる Spring Bean を宣言することができるようになります。 ただし、 Spring のコンポーネントモデルはステートフル性に対応するようには設計されたことはないため、 この機能を使用する際は十分に気を付けてください。 特に、 セッションや対話スコープの Spring Bean のクラスタ化は根深い問題があるため、 広いスコープの Bean やコンポーネントをスコープの狭い Bean にインジェクトする場合は注意が必要です。

Spring Bean factory 設定で<seam:configure-scopes/> を一度指定すると、 すべての Seam スコープがカスタムスコープとして Spring Bean に利用可能になります。 Spring Bean を特定の Seam スコープに関連付けるには、 Bean 定義の scope 属性で Seam スコープを指定してください。


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

...

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

configure-scopes 定義内の prefix 属性を指定することによって、 スコープ名のプレフィックスを変更することができます。 (デフォルトのプレフィックスは seam. です。)

デフォルトではこの方法で登録される Spring コンポーネントのインスタンスは @In を使って参照される場合に自動的には作成されません。 インスタンスを自動作成させるにはインジェクションポイントで @In(create=true) を指定して自動作成される特定 Bean を識別するか、 configure-scopesdefault-auto-create 属性を使って Seam スコープを使用する Spring Bean はすべて自動作成されるようにします。

この方法で定義された Seam スコープの Spring Bean は、 <seam:instance/> を使用することなく他の Spring Bean にインジェクト可能です。 ただし、 スコープインピーダンスが必ず維持されるよう十分に注意してください。 Spring で一般的に使用される方法は、 Bean 定義内での <aop:scoped-proxy/> の指定です。 しかし、 Seamスコープの Spring Bean は <aop:scoped-proxy/> との互換性がありません。 したがって、 単一の Bean に Seam スコープ Spring Beanをインジェクトする必要がある場合、 <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 は拡張可能なトランザクション管理抽象を提供し、 多くのトランザクション API (JPA、 Hibernate、 JDO、 JTA など) に対応します。 また、 Spring は Websphere や Weblogic などの多くのアプリケーションサーバーの TransactionManagers との堅固な統合を実現します。 Spring トランザクション管理はネストされるトランザクションなど多くの高度な機能のサポートを提供し、 REQUIRES_NEW や NOT_SUPPORTED のような完全 Java EE トランザクション伝播のルールに対応します。 詳細については Spring のドキュメント ここ を参照してください。

Seam が Spring のトランザクションを使用するよう設定するには、 SpringTransaction コンポーネントを以下のように有効にします。


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

spring:spring-transaction コンポーネントは同期のコールバックに Spring トランザクション同期の機能を利用します。

Seam のパワフルな機能のひとつにその対話スコープと対話が生きている間 EntityManager をオープンにしておく機能があります。 これによりエンティティの分離や再併合に関連する多くの問題が解消され、 深刻な LazyInitializationException の発生を軽減することができます。 Spring は Web の一要求のスコープを越えて永続コンテキストを管理する方法は提供していません (OpenEntityManagerInViewFilter)。 このため、 Spring が JPA との統合に提供しているのとすべて同じツールを使った Seam 管理の永続コンテキストへのアクセスを Spring 開発者に持たせることができるとよいでしょう (PersistenceAnnotationBeanPostProcessorJpaTemplate など)。

Seam は提供される JPA ツールを使って Spring が Seam 管理永続コンテキストにアクセスする方法を提供します。 これにより Spring アプリケーションに対して対話スコープの永続コンテキスト機能を実現します。

この統合作業により次のような機能を実現します。

Spring の永続コンテキスト伝播モデルは EntityManagerFactory ごと一つの EntityManager しかオープンにしないため、 Seam 統合は EntityManagerFactory を Seam 管理永続コンテキストでラップすることで動作します。


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

「persistenceContextName」は Seam 管理の永続コンテキストコンポーネントの名前になります。 デフォルトではこの EntityManagerFactory には Seam コンポーネント名と同等の unitName があり、 この場合は「entityManager」になります。 別の unitName を与えたい場合は次のようにして persistenceUnitName を与えることができます。


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

これでこの EntityManagerFactory をいずれの Spring 提供のツールでも使用することができるようになります。 たとえば、 Spring の PersistenceAnnotationBeanPostProcessor を使用すると前とまったく同じになります。


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

Spring では実際の EntityManagerFactory を定義するが Seam 管理の永続コンテキストを使用したい場合は defaultPersistenceUnitName プロパティを指定してデフォルトで使用したい persistenctUnitName を PersistenceAnnotationBeanPostProcessor に指示することができます。

applicationContext.xml は次に似たようなものになります。


<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 は次に似たようなものになります。


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

JpaTemplate および JpaDaoSupport は Seam 管理永続コンテキスト用となるため、 Seam 管理永続コンテキストに対して同じ方法で設定します。


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

Seam Spring 統合により Spring のツールを使った Seam 管理 Hibernate セッションへの完全アクセスに対応することもできます。 この統合は JPA 統合 に非常に似ています。

Spring の JPA 統合と同様に Spring の伝播モデルは一トランザクションの一EntityManagerFactory ごとに一つの EntityManager しか Spring ツールに対して利用できるようオープンにしません。 このため、 Seam Session 統合は proxy SessionFactory を Seam 管理の Hibernate セッションコンテキストでラップすることにより動作します。


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

「sessionName」は persistence:managed-hibernate-session コンポーネントの名前になります。 これでこの SessionFactory はいずれの Spring 提供ツールでも使用することができるようになります。 この統合は SeamManagedSessionFactory で getCurrentInstance() を呼び出している場合であれば SessionFactory.getCurrentInstance() に対する呼び出しにも対応します。

アプリケーションの持つ Spring の ApplicationContext を起動するために Spring の ContextLoaderListener を使用することはできますが、 制約がいくつかあります。

これら 2 つの制約を克服するために Spring 統合には Spring ApplicationContext を起動する Seam コンポーネントが含まれています。 この Seam コンポーネントを使用するには、 <spring:context-loader/> の定義を components.xml に配置します。 config-locations 属性で使用する Spring コンテキストファイルの場所を指定します。 複数の設定ファイルが必要な場合は、 ネストされる <spring:config-locations/> エレメントに配置することができます。 これを行うには、 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 はコードを非同期に実行するために TaskExecutor と呼ばれる抽象を提供します。 Spring Seam 統合では @Asynchronous メソッドのコールを直ちに実行するために Spring の TaskExecutor を使用できます。 この機能を有効にするには SpringTaskExecutorDispatchor をインストールしてから次のように Spring Bean 定義の taskExecutor を与えます。


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

Spring の TaskExecutor は非同期イベントのスケジューリングには対応しないため、 代替となる Seam Dispatcher を与えて次のようにスケジュールされた非同期イベントを処理することができます。


<!-- 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}"/>