如果你有大量的组件需要在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 name="hello">
<property name="name">#{user.name}</property>
</component>
在第二个选项中,类名与组件定义所在的文件是一致的。
你还可以选择将所有类的配置都放在 com/helloworld/components.xml 的 com.helloworld 包中。
可配置的属性类型
String的属性、基本类型以及基本类型的包装类型可以像我们期望的那样进行配置:
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>
也支持由String或者基本类型构成的数组、Set和List:
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 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>
甚至也支持那些包含String值为键、String或者基本类型值的Map:
<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所提供的依赖注入功能非常非常类似。
<drools:managed-working-memory name="policyPricingWorkingMemory" rule-base="#{policyPricingRules}"/>
<component name="policyPricingWorkingMemory"
class="org.jboss.seam.drools.ManagedWorkingMemory">
<property name="ruleBase">#{policyPricingRules}</property>
</component>
使用XML命名空间
纵观整个示例,有两种完全不同的声明组件的方式:使用或者不使用XML命名空间。 下面的示例展示了一个典型的 components.xml 文件,它没有使用命名空间,而是使用Seam Components DTD:
<?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内容是清晰而简单易懂的。 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定义可以当作示范。
第一个选项允许你在一个文件中定义多个组件:
<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/components.xml 的 com.helloworld 包中。
可配置的属性类型
String的属性、基本类型以及基本类型的包装类型可以像我们期望的那样进行配置:
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>
也支持由String或者基本类型构成的数组、Set和List:
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 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>
甚至也支持那些包含String值为键、String或者基本类型值的Map:
<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所提供的依赖注入功能非常非常类似。
<drools:managed-working-memory name="policyPricingWorkingMemory" rule-base="#{policyPricingRules}"/>
<component name="policyPricingWorkingMemory"
class="org.jboss.seam.drools.ManagedWorkingMemory">
<property name="ruleBase">#{policyPricingRules}</property>
</component>
使用XML命名空间
纵观整个示例,有两种完全不同的声明组件的方式:使用或者不使用XML命名空间。 下面的示例展示了一个典型的 components.xml 文件,它没有使用命名空间,而是使用Seam Components DTD:
<?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内容是清晰而简单易懂的。 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定义可以当作示范。