第5章 配置Seam组件
在Seam中,极小化的基于XML配置的价值体系是非常强壮的。不过,为什么我们想使用XML配置一个Seam组件,有多种理由:从Java代码分离出特定部署信息,使可重用框架的创作成为可能,配置Seam的内建功能,等等。Seam提供两个基本的方法配置组件:用一个属性文件设置属性或者用web.xml进行配置;通过components.xml进行配置。
5.1.通过设置属性配置组件
能够通过servlet上下文参数,或通过在类路径的根目录下的一个文件:seam.properties,两个方法之一给Seam组件提供配置属性。
可配置的Seam组件必须暴露JavaBeans类属性setter方法给可配置属性。如果一个Seam组件名为com.jboss.myapp.settings有一个setter方法名为setLocale(),我们能够提供一个名为com.jboss.myapp.settings.locale的属性在seam.properties文件内,或者用一个servlet上下文参数,只要Seam实例化组件,它会设置locale属性的值。
同样的机制常用来配置Seam自已。例如,设置对话空闲时间,我们用web.xml 或者 seam.properties为org.jboss.seam.core.manager.conversationTimeout提供一个值(有一个名为org.jboss.seam.core.manager的Seam组件带有一个名为setConversationTimeout()的setter方法)。
5.2.通过components.xml配置组件
components.xml文件比属性设置更有威力。它让你:
*配置已经自动安装的组件——包括内建组件和Seam部署扫描已获得的用@Name注释了的组件。
*安装没有@Name注释的类作为Seam组件——对确定结构类型组件这是最有用的,它能以不同的名字多次被安装(例如,Seam管理持久化上下文)。
* 除安装有@Name注释的组件之外,缺省是不安装的,因为@Install注释标明组件不会被安装。
*覆盖组件的范畴。
components.xml 文件可能会出现在三种不同的地方:
* 一个war 文件的WEB-INF目录。
* 一个jar包文件的META-INF 目录。
* 一个jar包文件中包含有@Name 注释的类的任一目录。
通常,当部署扫描发现参与在seam.properties文件或在META-INF/components.xml文件的文档中一个带有@Name注释类时,Seam组件被安装。(除非组件有一个@Install注释标明它在缺省时不安装。)components.xml文件让我们处理特殊情况,我们需要覆盖注释的地方。
例如,下面的components.xml文件安装jBPM:
<components xmlns="http://jboss.com/products/seam/components"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bpm="http://jboss.com/products/seam/bpm">
<bpm:jbpm/>
</components>
这个例子做同样的事情:
<components>
<component class="org.jboss.seam.bpm.Jbpm"/>
</components>
这是一个安装,配置了两个不同的Seam管理持久化上下文:
<components xmlns="http://jboss.com/products/seam/components"
xmlns:persistence="http://jboss.com/products/seam/persistence"
<persistence:managed-persistence-context name="customerDatabase"
persistence-unit-jndi-name="java:/customerEntityManagerFactory"/>
<persistence:managed-persistence-context name="accountingDatabase"
persistence-unit-jndi-name="java:/accountingEntityManagerFactory"/>
</components>
这一个也是做一样的事:
<components>
<component name="customerDatabase"
class="org.jboss.seam.persistence.ManagedPersistenceContext">
<property name="persistenceUnitJndiName">java:/customerEntityManagerFactory</
property>
</component>
<component name="accountingDatabase"
class="org.jboss.seam.persistence.ManagedPersistenceContext">
<property name="persistenceUnitJndiName">java:/accountingEntityManagerFactory</
property>
</component>
</components>
这个例子创建了一个会话范围的Seam管理持久化上下文(在实践中,这是不被推荐的):
<components xmlns="http://jboss.com/products/seam/components"
xmlns:persistence="http://jboss.com/products/seam/persistence"
<persistence:managed-persistence-context name="productDatabase"
scope="session"
persistence-unit-jndi-name="java:/productEntityManagerFactory"/>
</components>
<components>
<component name="productDatabase"
scope="session"
class="org.jboss.seam.persistence.ManagedPersistenceContext">
<property name="persistenceUnitJndiName">java:/productEntityManagerFactory</property>
</component>
</components>
对象持久上下文的这样的结构对象使用自动创建选项是普通的,节省了当你使用@In注释时明确指定create=true。
<components xmlns="http://jboss.com/products/seam/components"
xmlns:persistence="http://jboss.com/products/seam/persistence"
<persistence:managed-persistence-context name="productDatabase"
auto-create="true"
persistence-unit-jndi-name="java:/productEntityManagerFactory"/>
</components>
<components>
<component name="productDatabase"
auto-create="true"
class="org.jboss.seam.persistence.ManagedPersistenceContext">
<property name="persistenceUnitJndiName">java:/productEntityManagerFactory</property>
</component>
</components>
<factory>声明让你指定一个值或一个方法,在它第一次被引用时,其捆绑的表达式,求得上下文变量的初始化值。
<components>
<factory name="contact" method="#{contactManager.loadContact}"
scope="CONVERSATION"/>
</components>
你也能为一个Seam组件创建一个“别名”(第二个名字),像这样:
<components>
<factory name="user" value="#{actor}" scope="STATELESS"/>
</components>
你甚至能为一个普通使用的表达式创建一个“别名”:
<components>
<factory name="contact" value="#{contactManager.contact}" scope="STATELESS"/>
</components>
伴有auto-create="true"的<factory>声明的使用尤其普遍:
<components>
<factory name="session" value="#{entityManager.delegate}" scope="STATELESS" auto-
create="true"/>
</components>
有时候,在部署和测试两个期间,我们想重用有很小变化的components.xml文件。Seam让你在components.xml文件设置@wildcard@通配符,在部署期间,用你的Ant构建脚本或用在类路径中的名为components.properties文件提供的内容替换它。你会在Seam例子中看到这种方法的使用。
5.3. 细粒度配置文件
如果你有大量的组件,需要用XML进行配置,分解在components.xml的信息成更多更小的文件会适合更多的情形。Seam让你为一个命名类设置配置,例如,在资源里的com.helloworld.Hello命名为com/helloworld/Hello.component.xml(使用这种模式你可能是熟悉的,因为它和我们在Hibernate的使用是一样的。)文件里的根元素可能是<components> 或者<component>元素。
在文件里的第一个选项让你定义多个组件:
<components>
<component class="com.helloworld.Hello" name="hello">
<property name="name">#{user.name}</property>
</component>
<factory name="message" value="#{hello.message}"/>
</components>
第二个选项只让你定义或者配置一个组件,除此之外是简洁的:
<component >
<property name="name">#{user.name}</property>
</component>
在第二个选项里,类名字被组件定义显示的那个文件暗示。
作为选择,你可能在com/helloworld/components.xml里为com.helloworld包内所有类设置配置。
5.4. 可配置属性类型
字符串属性,原始的或原始包裹类型可以象你希望那样被配置:
org.jboss.seam.core.manager.conversationTimeout 60000
<core:manager conversation-timeout="60000"/>
<component >
<property name="conversationTimeout">60000</property>
</component>
数组,集和字符链表或者原始的类型也被支持:
org.jboss.seam.bpm.jbpm.processDefinitions order.jpdl.xml, return.jpdl.xml, inventory.jpdl.xml
<bpm:jbpm>
<bpm:process-definitions>
<value>order.jpdl.xml</value>
<value>return.jpdl.xml</value>
<value>inventory.jpdl.xml</value>
</bpm:process-definitions>
</bpm:jbpm>
<component >
<property name="processDefinitions">
<value>order.jpdl.xml</value>
<value>return.jpdl.xml</value>
<value>inventory.jpdl.xml</value>
</property>
</component>
甚至,带有字符值键和字符或原始类的值的映射也被支持:
<component >
<property name="issueStatuses">
<key>open</key> <value>open issue</value>
<key>resolved</key> <value>issue resolved by developer</value>
<key>closed</key> <value>resolution accepted by user</value>
</property>
</component>
最后,你可以使用一个捆绑值表达式网绑组件在一起。注意这是完全不同于@In注入的使用,因为它发生在组件实例化的时间,而不是在调用的时间。因而它更相似于传统的IoC(倒置控制)如JSF或Spring提供的依赖注入方法。
<drools:managed-working-memory name="policyPricingWorkingMemory" rule-base="#{policyPricingRules}"/>
<component
class="org.jboss.seam.drools.ManagedWorkingMemory">
<property name="ruleBase">#{policyPricingRules}</property>
</component>
5.5. 使用 XML 命名空间
全部例子,存在两个声明组件的相竞争的方法:用和不用XML 命名空间。下面是显示了一个典型的没有使用命名空间的components.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
xsi:schemaLocation="http://jboss.com/products/seam/components http://jboss.com/
products/seam/components-2.1.xsd">
<component class="org.jboss.seam.core.init">
<property name="debug">true</property>
<property name="jndiPattern">@jndiPattern@</property>
</component>
</components>
正如你看见的,这是有些冗余。甚至更糟,在部署时,组件和属性名字不能被校验。
命名空间版看起来象这样:
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd
http://jboss.com/products/seam/components http://jboss.com/products/seam/
components-2.1.xsd">
<core:init debug="true" jndi-pattern="@jndiPattern@"/>
</components>
即使schema计划声明是冗余的,实际的XML内容是瘦小的和容易理解的。schema计划提供了关于每一个组件和属性变量详细的信息,允许XML编辑器支持智能自动完善。命名空间元素的使用使得产生和维护正确的components.xml更简单。
目前,大部分工作是对Seam内建组件的,然而对用户组件呢?有两个选项。第一,Seam支持混合两个模式,允许对用户组件使用普通的<component>声明,连同对内建组件命名空间声明一起。其次,甚至更好,Seam允许你为你自己的组件快速声明命名空间。
任何Java包能通过用@Namespace注释注释包与XML命名空间相关联(包级注释被声明在包目录下的一个名为package-info.java文件中)。这儿是一个来自seampay演示的例子:
@Namespace(value="http://jboss.com/products/seam/examples/seampay")
package org.jboss.seam.example.seampay;
import org.jboss.seam.annotations.Namespace;
这是所有你需要在components.xml中使用的命名空间的样式。现在我们能写出:
<components xmlns="http://jboss.com/products/seam/components"
xmlns:pay="http://jboss.com/products/seam/examples/seampay"
... >
<pay:payment-home new-instance="#{newPayment}"
created-message="Created a new payment to #{newPayment.payee}" />
<pay:payment name="newPayment"
payee="Somebody"
account="#{selectedAccount}"
payment-date="#{currentDatetime}"
created-date="#{currentDatetime}" />
...
</components>
或者:
<components xmlns="http://jboss.com/products/seam/components"
xmlns:pay="http://jboss.com/products/seam/examples/seampay"
... >
<pay:payment-home>
<pay:new-instance>"#{newPayment}"</pay:new-instance>
<pay:created-message>Created a new payment to #{newPayment.payee}</pay:created-
message>
</pay:payment-home>
<pay:payment name="newPayment">
<pay:payee>Somebody"</pay:payee>
<pay:account>#{selectedAccount}</pay:account>
<pay:payment-date>#{currentDatetime}</pay:payment-date>
<pay:created-date>#{currentDatetime}</pay:created-date>
</pay:payment>
...
</components>
这些例子阐明了两种命名空间元素的使用模式。在第一个声明,<pay:payment-home>引用了paymentHome组件:
package org.jboss.seam.example.seampay;
...
@Name("paymentHome")
public class PaymentController
extends EntityHome<Payment>
{
...
}
元素名是由组件名与连字符构成。元素的属性是由属性名与连字符构成。
在第二个声明,<pay:payment>元素引用了在org.jboss.seam.example.seampay包中的Payment类。在这种情况下,Payment是一个实体被声明成一个Seam组件:
package org.jboss.seam.example.seampay;
...
@Entity
public class Payment
implements Serializable
{
...
}
如果我们想校验和自动完善用户定义组件的使用,我们将需要一个schema计划。Seam并没有为一组组件提供一个机制来自动产生一个schema计划。为标准Seam包的schema计划定义能用来做为指导。
下面是Seam使用的命名空间:
* components — http://jboss.com/products/seam/components
* core — http://jboss.com/products/seam/core
* drools — http://jboss.com/products/seam/drools
* framework — http://jboss.com/products/seam/framework
* jms — http://jboss.com/products/seam/jms
* remoting — http://jboss.com/products/seam/remoting
* theme — http://jboss.com/products/seam/theme
* security — http://jboss.com/products/seam/security
* mail — http://jboss.com/products/seam/mail
* web — http://jboss.com/products/seam/web
* pdf — http://jboss.com/products/seam/pdf
* spring — http://jboss.com/products/seam/spring