在seam中,对基于XML的设置予以简化,其作用是非常巨大的。然而,有很多原因,使我们应当用XML来设置seam组件:从java代码中分离出特定的部署信息,建立复用框架,设置seam的内建功能,等等。对于设置组件,Seam提供两种基本的途径:通过设置属性文件或web.xml里的属性,以及通过设置components.xml文件。
4.1.通过属性设定来设置组件
提供Seam组件时可以带有属性配置,它们是由servlet context参数,或者classpath根部的seam.properties文件提供的。可配置的seam组件必须为可配置的属性建立JavaBeans类型的setter方法。如果一个名称为com.jboss.myapp.settings 的seam组件有一个名称为setLocale()的setter方法,我们就可以在seam.properties文件中提供一个名称为com.jboss.myapp.settings.locale的属性,seam在实例化这个组件时,将据此设置locale属性的值。
设置seam本身时也使用了这种机制,例如,为设置会话时间,我们在web.xml或seam.properties为org.jboss.seam.core.manager.conversationTimeout提供了一个值。(seam
中有一个名为org.jboss.seam.core.manager的seam组件,它有一个名为setConversationTimeout()的setter方法)
4.2.通过components.xml配置组件
components.xml文件比属性设定更强大一些。它可以让你:
• 设置已经自动安装的组件—包括内建的组件,和用@Name标注了的和seam的部署扫描器安装的应用程序组件。
• 把没有用@Name标注的类安装为seam组件--这对某些特定的底层组件非常有用,这种底层组件可以用不同的名称安装多次(例如Seam-managed persistence contexts)。
• 安装确实有@Name标注,但是由于有@Install标注(表明这个组件不应该安装)默认没被安装的组件。
• 覆盖组件的生命期。
一个components.xml文件可能出现在以下位置:
• War文件的WEB-INF目录。
• Jar文件的META-INF目录。
• 包含具有@Name标注的类的jar文件的任何目录。
For example, the following components.xml file installs jBPM:
一般说来,当部署扫描器发现一个含有@Name标注的类,这个类在seam.properties 或META-INF/components.xml文件中作了登记,部署扫描器将安装这个类(除非这个组件有@Instal标注,表明它默认不应安装)。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-managed persistence contexts:
<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>
这个例子建立了一个session会话期的Seam-managed persistence context(实践中不推荐这么做):
<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>
对基层对象,比如persistence contexts,通常可以用auto-create 选项。使用persistence contexts的一个好处是,当你使用@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>声明用于指定一个值或方法表达式,以便在第一次被引用时初始化context变量的值。
<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>
带有<factory> auto-create="true"参数的<factory>声明尤其常见:
<components>
<factory name="session" value="#{entityManager.delegate}" scope="STATELESS" auto-create="true"/>
</components>
4.3. 细粒度配置文件
如果你需要用XML配置大量的组件,那么把components.xml中的信息分离开来是很有必要的。例如,可以把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 name="hello">
<property name="name">#{user.name}</property>
</component>
第二种方式中,类名称由组件定义文件来暗示。
你也可以把com.helloworld 包中的所有类设置放到 com/helloworld/components.xml 中。
4.4. 可以设置的属性类型
如你所愿,可以设置的属性类型有string, primitive 或 primitive wrapper:
org.jboss.seam.core.manager.conversationTimeout 60000
<core:manager conversation-timeout="60000"/>
<component name="org.jboss.seam.core.manager">
<property name="conversationTimeout">60000</property>
</component>
也支持Arrays, sets 和 lists of strings 或 primitives:
org.jboss.seam.bpm.jbpm.processDefinitions order.jpdl.xml, return.jpdl.xml, inventory.jpdl.xml
<core:jbpm>
<core:process-definitions>
<value>order.jpdl.xml</value>
<value>return.jpdl.xml</value>
<value>inventory.jpdl.xml</value>
</core:process-definitions>
</core:jbpm>
<component name="org.jboss.seam.bpm.jbpm">
<property name="processDefinitions">
<value>order.jpdl.xml</value>
<value>return.jpdl.xml</value>
<value>inventory.jpdl.xml</value>
</property>
</component>
支持Even maps with String-valued keys 和 string 或 primitive 值:
<component name="issueEditor">
<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提供的依赖注入环境(dependency injection facilities)更相似。
<drools:managed-working-memory name="policyPricingWorkingMemory" rule-base="#{policyPricingRules}"/>
<component name="policyPricingWorkingMemory"
class="org.jboss.seam.drools.ManagedWorkingMemory">
<property name="ruleBase">#{policyPricingRules}</property>
</component>
4.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.0.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.0.xsd
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd">
<core:init debug="true" jndi-pattern="@jndiPattern@"/>
</components>
虽然对schema的声明较长,不过真正的XML内容较短且易懂。Schemas提供了关于每个组件和属性的详细信息,允许XML编辑器提供自动完成(intelligent autocomplete)功能。命名空间元素的使用使生成与维护正确的components.xml文件变得非常容易。
现在,对内建seam组件来说,这种机制已经非常完善,但是,用户组件如何呢?有两个选项。第一,seam支持两种模型的混合,允许对用户组件使用普通的<component>声明,同时对内建seam组件使用命名空间声明。更妙的是,seam允许对你自己的组件快速声明命名空间。
用@Namespace标注过的任何Java package皆可以关联一个XML命名空间。(包目录中package-info.java文件可以声明Package-level annotations)这里是取自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组件的entity:
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