领先一步
VMware提供培训和认证,以加速你的进步。
了解更多在所有新的Spring 2.0特性和改进中,我必须承认消息驱动的POJO是我个人最喜欢的之一。我感觉很多其他的Spring用户也会有同样的感觉。
在这里我提供了一个快速介绍。还有很多内容需要展示,我将在后续的文章中继续介绍。不过就目前而言——这应该能为你提供足够的信息,让你开始使用真正基于POJO的异步JMS!我希望你和我一样对此感到兴奋;)
你需要在你的类路径上包含以下JAR文件。我还列出了我正在使用的版本(任何spring-2.x版本都应该没问题。实际上,我刚刚在两分钟前放入了RC3)
首先,我们需要设置环境。我将使用ActiveMQ,但是更改提供程序的影响将仅限于此一个文件的修改。我将此文件命名为“shared-context.xml”,因为正如你很快就会看到的,我将为JMS通信的两端导入这些bean定义。以下是“共享”bean定义:连接工厂和两个队列(一个用于请求,一个用于回复)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.xsd">
<bean id="requestQueue" class="org.activemq.message.ActiveMQQueue">
<constructor-arg value="requestQueue"/>
</bean>
<bean id="replyQueue" class="org.activemq.message.ActiveMQQueue">
<constructor-arg value="replyQueue"/>
</bean>
<bean id="connectionFactory" class="org.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://127.0.0.1:61616"/>
</bean>
</beans>
如你所见,我将在tcp上运行ActiveMQ(我只是从分发的bin目录中运行'activemq')。也可以运行嵌入式(使用“vm://127.0.0.1”代替)——或者你可以运行org.activemq.broker.impl.Main类的main方法。如果你想获取分发版,请访问:http://www.activemq.org。
我在这里有意保持简单——主要目标是演示各个部分如何组合在一起。不过,我想指出的最重要的一点是,我的“域”中的这些类是POJO。你根本不会看到任何Spring或JMS依赖项。
最终,我们将接受用户的输入(通过stdin的“名称”),并将此转换为某个未指定事件的“注册请求”。消息将异步发送,但我们将有另一个队列来处理回复。然后,ReplyNotifier将确认(或“未确认”消息)写入stdout。
顺便说一下,我正在“blog.mdp”包中创建所有这些类。第一个类是RegistrationRequest
package blog.mdp;
import java.io.Serializable;
public class RegistrationRequest implements Serializable {
private static final long serialVersionUID = -6097635701783502292L;
private String name;
public RegistrationRequest(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
接下来是RegistrationReply
package blog.mdp;
import java.io.Serializable;
public class RegistrationReply implements Serializable {
private static final long serialVersionUID = -2119692510721245260L;
private String name;
private int confirmationId;
public RegistrationReply(String name, int confirmationId) {
this.name = name;
this.confirmationId = confirmationId;
}
public String toString() {
return (confirmationId >= 0)
? name + ": Confirmed #" + confirmationId
: name + ": Not Confirmed";
}
}
以及RegistrationService
package blog.mdp;
import java.util.HashMap;
import java.util.Map;
public class RegistrationService {
private Map registrations = new HashMap();
private int counter = 100;
public RegistrationReply processRequest(RegistrationRequest request) {
int id = counter++;
if (id % 5 == 0) {
id = -1;
}
else {
registrations.put(new Integer(id), request);
}
return new RegistrationReply(request.getName(), id);
}
}
如你所见,这仅仅提供了一个示例。实际上,可能会对registrations映射做一些操作。此外,你看到20%的注册尝试将被拒绝(给定-1确认Id)——这并不是处理注册请求的实用方法,但它将为回复消息提供一些多样性。同样,重要的是此服务类与Spring或JMS没有任何联系。然而,正如你稍后将看到的,它将处理通过JMS发送的消息的有效负载。换句话说,此RegistrationService是消息驱动的POJO。
最后,创建一个简单的类来记录回复消息
package blog.mdp;
public class ReplyNotifier {
public void notify(RegistrationReply reply) {
System.out.println(reply);
}
}
现在是最重要的部分。我们如何使用Spring配置POJO服务,使其接收JMS消息?答案以2个bean定义的形式出现(如果你将服务本身也算在内,则为3个)。在这个下一个bean定义文件中,请注意“容器”,它实际上接收消息并启用异步监听器的使用。容器需要知道它从中接收消息的连接工厂和目标。有多种类型的容器可用,但这超出了本博客的范围。阅读参考文档以获取更多信息:消息监听器容器。
在这种情况下,“监听器”是Spring的MessageListenerAdapter的一个实例。它引用了委托(POJO服务)和处理程序方法的名称。在这种情况下,我们还提供了一个defaultResponseDestination。对于返回void的方法,你显然不需要这样做。此外(在生产应用程序中可能更常见),你可以省略它,而是设置传入JMS消息的“reply-to”属性。
现在我们已经讨论了各种参与者,以下是bean定义(我将此文件命名为“server-context.xml”)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.xsd">
<import resource="shared-context.xml"/>
<bean id="registrationService" class="blog.mdp.RegistrationService"/>
<bean id="listener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="registrationService"/>
<property name="defaultListenerMethod" value="processRequest"/>
<property name="defaultResponseDestination" ref="replyQueue"/>
</bean>
<bean id="container" class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="messageListener" ref="listener"/>
<property name="destination" ref="requestQueue"/>
</bean>
</beans>
这里的最后一步是提供一个用于运行服务的引导机制,因为这是一个简单的独立示例。我刚刚创建了一个微不足道的main方法来启动具有相关bean定义的ApplicationContext,然后阻塞
package blog.mdp;
import java.io.IOException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RegistrationServiceRunner {
public static void main(String[] args) throws IOException {
new ClassPathXmlApplicationContext("/blog/mdp/server-context.xml");
System.in.read();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.xsd">
<import resource="shared-context.xml"/>
<bean id="replyNotifier" class="blog.mdp.ReplyNotifier"/>
<bean id="listener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="replyNotifier"/>
<property name="defaultListenerMethod" value="notify"/>
</bean>
<bean id="container" class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="messageListener" ref="listener"/>
<property name="destination" ref="replyQueue"/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="defaultDestination" ref="requestQueue"/>
</bean>
</beans>
那里定义了另一个bean——Spring的“jmsTemplate”的一个实例。我们将使用它将注册请求消息发送到其defaultDestination。借助Spring提供的简单convertAndSend(..)方法,发送JMS消息非常简单。我创建了一个类,它接受用户输入,然后使用此“jmsTemplate”发送消息
package blog.mdp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
public class RegistrationConsole {
public static void main(String[] args) throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("/blog/mdp/client-context.xml");
JmsTemplate jmsTemplate = (JmsTemplate) context.getBean("jmsTemplate");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
for (;;) {
System.out.print("To Register, Enter Name: ");
String name = reader.readLine();
RegistrationRequest request = new RegistrationRequest(name);
jmsTemplate.convertAndSend(request);
}
}
}