第27章. Spring框架集成

27章. Spring框架集成


 

Spring集成模块可轻松地移植基于Spring的项目到Seam, 并允许Spring应用程序利用Seam的关键功能,如对话和Seam更先进的持久化上下文管理。

 

注意! Spring 集成代码包含在jboss-seam-ioc库中。这种依赖关系需要本章涉及到的所用seam-spring集成技术。

 

Seam对Spring的支持,提供的能力有:

  • 注入Seam组件的实例到Spring beans
  • 注入Spring beans到Seam组件
  • 转换Spring beans为Seam组件
  • 允许Spring beans存活在任何Seam上下文中
  • 用一个Seam组件启动一个spring WebApplicationContext
  • 支持Spring PlatformTransactionManagement
  • Spring的 OpenEntityManagerInViewFilter和 OpenSessionInViewFilter提供一个Seam管理替代物
  • 支持Spring TaskExecutors返回@Asynchronous调用
  •  

27.1. 注入Seam组件到Spring beans

 

使用<seam:instance/>命名空间处理器完成注入Seam组件到Spring beans 。为启用Seam命名空间处理器,Seam命名空间必须增加到Spring beans定义文件:

 

 

<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 beans 。

 

<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对话作用域(CONVERSATION-scoped)组件实例被直接注入到一个单例Spring bean,在对话结束后,这个单独Spring bean会长期维持同一个实例的一个引用。我们称这个问题为作用域阻抗(cope impedance)。Seam双向注入确保作用域阻抗(cope impedance)作为贯穿系统的一个调用流被自然管理。在Spring中,我们需要注入一个Seam组件代理,并且在代理被调用时解析这个引用。

 

<seam:instance/> 标签让我们自动代理组件。

 

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

 

<bean id="someSpringBean" class="SomeSpringBeanClass">

 

    <property name="entityManager" ref="seamManagedEM">

 

</bean>

 

这个例子显示了一个方法,从一个Spring bean使用一个Seam管理(Seam-managed)持久化上下文。 (有关使用Seam管理(Seam-managed)持久化上下文替代Spring OpenEntityManagerInView过滤器的更有力的方法,请看在Spring中使用Seam管理持久化上下文)

 

27.2. 注入Spring beans到Seam组件

 

注入Spring beans到Seam组件实例更容易。实际上,有两种方法:

 

  • 利用EL表达式注入一个Spring bean
  • 使Spring bean成为一个Seam组件

 

第二个方法我们将在下节讨论。最容易的方法是通过EL访问Spring beans。

 

Spring的DelegatingVariableResolver是其提供来整合JSF的集成点。 这个VariableResolver 让所有Spring beans通过它们的bean id在EL被应用。你需要增加 DelegatingVariableResolver到faces-config.xml:

 

<application>

 

    <variable-resolver>

 

        org.springframework.web.jsf.DelegatingVariableResolver

 

    </variable-resolver>

 

</application>

 

然后你可以利用@In 注入Spring beans :

 

@In("#{bookingService}")

 

private BookingService bookingService;

 

Spring beans在EL中的使用不局限于注入这种情况。 Spring beans可以用于Seam中使用EL表达式的任何地方 :过程和页面流定义,工作内存声明(assertions)等等。

 

27.3. 使Spring bean成为一个Seam组件

 

<seam:component/>命名空间处理器可以被用来使Spring bean成为一个Seam组件。 只需放<seam:component/> 标签在你希望成为一个Seam 组件的bean的声明内:

 

<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">

 

    <seam:component/>

 

</bean>

 

默认时, <seam:component/>会用在bean定义中提供的类和名字创建一个无状态Seam组件。有时候,例如当使用FactoryBean, Spring bean的类可以不是出现在bean定义中的类。在这种情况,类应该明确定义。在有命名冲突的情况下,Seam组件名可明确指定 。

如果你希望Spring bean被管理在一个特殊的Seam作用域,可以使用<seam:component/> 的scope 属性。如果Seam 指定的作用域不是无状态作用域, Spring bean必须用原型限定,先存在的 Spring beans通常有一个无状态字符, 因此通常不需要这个属性。

 

27.4. Seam作用域的Spring Bean

 

Seam 集成包也允许你使用Seam的上下文作为Spring 2.0类型的自定义作用域。这允许你在所有Seam 上下文中声明任何Spring bean 。然而再次告诫,Spring 组件模型绝不是被构建来支持有状态的,所以,请小心使用这个功能。特别是,会话群集或对话作用域的Spring beans是大有问题的,当从一个大的作用域注入一个bean或组件到一个小的作用域时,必须要很小心。

 

一旦在Spring bean factory 配置中指定了<seam:configure-scopes/>,任何Seam作用域都将会被作为Spring beans的自定义作用域来使用。为了让Spring bean与一个特殊的Seam作用域关联,用bean定义的scope 属性来指定Seam作用域:

 

<!—每个bean factory 只需要指定一次-->

 

<seam:configure-scopes/>

 

...

 

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

 

通过在configure-scopes 的定义中指定prefix 属性,作用域名的前缀可以被改变。 (默认前缀是seam。)

默认时,当利用@In引用时,以这种方式注册的Spring组件的一个实例不会被自动创建。 为了让实例自动创建,你必须在注入点指定@In(create=true)来标明一个特殊的bean会被自动创建,或者利用configure-scopes的default-auto-create属性,让所有使用seam 作用域的spring beans自动创建。

 

用这种方式定义的Seam作用域Spring beans, 不用<seam:instance/>就可以注入到另外的Spring beans中。然而,必须注意确保作用域阻抗被维持。在Spring中使用的一般方法是在bean定义中指定 <aop:scoped-proxy/>。可是,Seam作用域的Spring beans是不兼容<aop:scoped-proxy/>。所以,如果你需要向一个单例中注入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>

 

 

27.5. 使用Spring PlatformTransactionManagement

 

Spring提供了一个扩展事务管理抽象,支持多种事务APIs (JPA, Hibernate, JDO, 和 JTA)。 Spring还提供了与多种应用服务器的事务管理器的紧密集成,如Websphere和Weblogic。 Spring事务管理接口(exposes)支持多种高级功能,如嵌套事务,并且支持完全的Java EE事务传播规则,如REQUIRES_NEW和 NOT_SUPPORTED。更多的信息,见Spring文档

 

激活SpringTransaction组件使用Spring事务 ,如下配置Seam:

 

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

 

spring:spring-transaction组件将利用Springs事务同步能力实现同步回调用 。

 

27.6. 在Spring中使用Seam管理的持久化上下文

 

Seam最强大的功能之一是它的对话作用域(conversation scope)和为对话存活期提供一个开放的实体管理器(EntityManager)的能力。这消除了大部分与实体关联的分离和重组问题,也减轻了恐怖的LazyInitializationException 的出现。Spring没有为超出单个网页请求的作用域提供管理持久化上下文的方法(OpenEntityManagerInViewFilter)。因此,如果Spring 开发者可以利用Spring提供的与JPA集成的完全一样的工具来访问Seam管理的持久化上下文,这将是很不错的(例如,PersistenceAnnotationBeanPostProcessor, JpaTemplate, 等等)。

 

Seam利用Spring JPA工具为Spring提供了一个访问Seam管理的持久化上下文的方法,让Spring应用程序拥有了对话作用域持久化上下文。

 

这个集成工作提供了下列功能:

 

  • 利用Spring 提供的工具,透明地访问Seam管理的持久化上下文
  • 用一个非网页请求访问Seam对话作用域持久化上下文 (例如,异步quartz job)
  • 允许Spring 管理的事务使用Seam管理的持久化上下文 (将需要手动刷新持久化上下文)

 

Spring持久化上下文传播模型只允许每个EntityManagerFactory有一个开放的EntityManager,所以, Seam集成通过围绕Seam管理的持久化上下文封装了一个EntityManagerFactory进行工作。

 

<bean id="seamEntityManagerFactory" class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">

 

    <property name="persistenceContextName" value="entityManager"/>

 

</bean>

 

这里, “persistenceContextName” 是Seam管理的持久上下文组件的名字 。默认时,这个EntityManagerFactory有一个单元名字(unitName)等价于Seam组件名,本例是“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提供的任何工具中。例如,使用SpringPersistenceAnnotationBeanPostProcessor是完全与前面相同的。

 

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

 

如果你在Spring中定义了你的真实EntityManagerFactory,并只希望使用Seam管理持久化上下文 ,你可以通过用你希望使用的persistenctUnitName默认指定defaultPersistenceUnitName属性告诉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}"/>

 

Seam 管理持久化上下文,用不变的方法配置JpaTemplate和JpaDaoSupport,因为它们是在Seam 管理持久化上下文之前。

 

<bean id="bookingService" class="org.jboss.seam.example.spring.BookingService">

 

    <property name="entityManagerFactory" ref="seamEntityManagerFactory"/>

 

</bean>

 

 

27.7. 在Spring 中使用Seam 管理的Hibernate 会话


 

Seam Spring集成也完全支持访问Seam 管理的Hibernate 会话, 通过使用spring工具。这个集成是非常相似于JPA integration

 

正如Spring JPA集成一样,Spring传播模式只允许每事务、每EntityManagerFactory、一个开放的EntityManager可用于Spring工具。所以,Seam Session集成通过围绕一个Seam管理的Hibernate会话上下文封装一个代理SessionFactory进行工作。

 

<bean id="seamSessionFactory" class="org.jboss.seam.ioc.spring.SeamManagedSessionFactoryBean">

 

    <property name="sessionName" value="hibernateSession"/>

 

</bean>

 

在这里“sessionName”是持久化:managed-hibernate-session 组件的名字。然后,这个SessionFactory 可用于任何Spring提供的工具。该集成也提供调用SessionFactory.getCurrentInstance()的支持, 只要你调用SeamManagedSessionFactory 的getCurrentInstance()



27.8. Spring 应用上下文作为一个Seam组件


 

虽然可以使用Spring的 ContextLoaderListener 启动你的应用程序的Spring ApplicationContext,然而有几个局限性:

  • Spring ApplicationContext必须在SeamListener之后启动
  • 启动Spring ApplicationContext,Seam单元和集成测试中使用可能会非常棘手。

为克服这两个局限性, Spring集成包含了一个Seam组件,用于启动一个Spring ApplicationContext。为使用这个Seam组件,在components.xml中设置<spring:context-loader/> 定义。在config-locations属性中指定你的Spring上下文的位置。如果你需要的配置文件超过一个,根据标准的components.xml的多值实践,你可以用嵌套的<spring:config-locations/>元素设置它们。

 

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

 

 

27.9. 为@Asynchronous使用Spring TaskExecutor


Spring提供了一个名为TaskExecutor的抽象用于异步执行代码。Spring Seam 集成允许使用 TaskExecutor来立即执行 @Asynchronous方法调用。为了激活这个功能,安装SpringTaskExecutorDispatchor ,并提供一个定义了taskExecutor 的spring bean,如下这样:

 

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

 

因为一个Spring TaskExecutor不支持异步事件的调度, 所以,提供了一个回调Seam分配器,可以被用来处理异步事件调度,如下这样:

 

            <!—安装一个ThreadPoolDispatcher,处理异步事件的调度。 -->

 

<core:thread-pool-dispatcher name="threadPoolDispatcher"/>

 

<!—安装此SpringDispatcher作为默认的 -->

 

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值