EJB 2.1 消息驱动 bean
那么在 J2EE 1.4 的 EJB 2.1 中发生了什么变化呢?有一个重大的区别:MDB 现在可以实现任何接口。没错 —— 任何 接口。例如,JCA 公共客户端接口(CCI)定义了清单 4 所示的接口,希望由 CCI 连接器异步驱动的 MDB 应当实现这些接口:
清单 4. javax.resource.cci.MessageListener 接口
2 Record onMessage(Record inputData)
3 throws ResourceException;
4 }
CCI 接口展示了新的灵活性的一些好处。首先,不需要把 MDB 传递给 JMS 消息。在清单 4 的接口中,传入一个 Record 对象。JMS 消息到达目标时也无需触发方法调用。对于 MDB 会在什么情况下驱动,JCA 规范没有做任何限制。它可以是资源适配器接收到后端系统的一些外部刺激之后的结果,也可以仅仅是一些内部事件,甚至可能是由计时器驱动的事件。第二,如清单 4 所示,调用的方法也可以拥有返回参数。在这种情况下,就可以传递回第二个 Record 对象,正如清单 5 的 ExampleListener 接口中的第一个方法所示,类型无需相同:
清单 5. listener 接口示例
2 ExampleOutput process(ExampleInput input);
3 String getData();
4 void request(int id, ExampleRequest request);
5 }
通常应当用方法的返回参数向造成方法调用事件的发起者提供响应,不论它是资源适配器还是其他什么系统。但是,就像 ExampleListener 接口的第二个方法所表现的那样,实际上可以不用任何参数调用 MDB,只用它检索一些值。第三个方法表明:方法可以使用的参数个数没有限制。这意味着资源适配器可以把初始事件解析为将传递给 MDB 的多个值。可以看到,方法的参数可以是原生类型或是对象。最后,即使 ExampleListener 接口有多个方法,使用它也是正确的。这样就可以让资源适配器根据事件判断要调用哪个方法。
既然 MDB 接口中消除了与 JMS 有关的限制,那么将部署描述符放在哪呢?清单 6 显示了一个 EJB 2.1 部署描述符示例,针对的是实现 ExampleListener 接口的 MDB:
清单 6. EJB 2.1 部署描述符
2 <enterprise-beans>
3 <message-driven>
4 <ejb-name>Example MDB</ejb-name>
5 <ejb-class>example.ExampleMdb</ejb-class>
6 <messaging-type>example.ExampleListener</messaging-type>
7 <transaction-type>Bean</transaction-type>
8 <activation-config>
9 <activation-config-property>
10 <activation-config-property-name>
11 HostName
12 </activation-config-property-name>
13 <activation-config-property-value>
14 example.com
15 </activation-config-property-value>
16 </activation-config-property>
17 <activation-config-property>
18 <activation-config-property-name>
19 DefaultId
20 </activation-config-property-name>
21 <activation-config-property-value>
22 2001
23 </activation-config-property-value>
24 </activation-config-property>
25 </activation-config>
26 </message-driven>
27 </enterprise-beans>
28 </ejb-jar>
29
在清单 6 中可以看到,messaging-type 元素包含 MDB 实现的那些接口的类名。如果遗漏了这个元素,那么可以采用 javax.jms.MessageListener 的旧值。特定于 JMS 的元素已经被一组通用的名称-值对替代,这个名称-值对叫作 激活配置属性(activation-configuration properties)。在清单 6 中可以看到,为 HostName 和 DefaultId 属性分别指定了值 example.com 和 2001。
可以编写实现任意接口的 MDB,这听起来太棒了,感觉不像是真的——可是它确实是真的。接下来的事可能就不太让人惊讶了:需要找到一个准备调用这个接口的资源适配器。资源适配器用它的部署描述符中的 messagelistener-type 元素表示它准备支持的接口(可能不止一个接口),如清单 7 所示:
清单 7. 输入资源适配器的部署描述符
2 <display-name>Example Inbound Resource Adapter</display-name>
3 <vendor-name>Example COM</vendor-name>
4 <eis-type>Example EIS</eis-type>
5 <resourceadapter-version>1.0</resourceadapter-version>
6 <resourceadapter>
7 <resourceadapter-class>
8 example.ExampleRaImpl
9 </resourceadapter-class>
10 <inbound-resourceadapter>
11 <messageadapter>
12 <messagelistener>
13 <messagelistener-type>example.ExampleListener</messagelistener-type>
14 <activationspec>
15 <activationspec-class>example.ExampleActivationSpecImpl</activationspec-class>
16 <required-config-property>
17 <config-property-name>HostName</config-property-name>
18 </required-config-property>
19 <required-config-property>
20 <config-property-name>PortNumber</config-property-name>
21 </required-config-property>
22 </activationspec>
23 </messagelistener>
24 </messageadapter>
25 </inbound-resourceadapter>
26 </resourceadapter>
27 </connector>
28
详细看看这个示例部署描述符是值得的。它从一些传统的东西开始:显示名称、供应商名称、资源适配器连接的企业信息系统(EIS)的名称,以及适配器的版本。这些内容后面跟着一个 resourceadapter 元素,它的第一个子元素包含 ResourceAdapter 接口的适配器实现的名称。后面跟着的是 inbound-resourceadapter 元素,它可以包含多个 messageadapter 实例。每个 messageadapter 实例都与一个 messagelistener-type 相关联。所以通过拥有多个 messageadapter 元素,可以编写一个输入资源适配器,支持多个接口。但是,必须要保证每个 messageadapter 指定一个不同的接口。
每个 messageadapter 还包含实现 ActivationSpec 接口的类的名称。在下一节中,我将介绍如何用这个类包含每个部署的 MDB 的配置信息。