領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多在所有新的 Spring 2.0 功能和改進中,我必須承認訊息驅動 POJO 是我個人最喜歡的功能之一。我有一種感覺,許多其他 Spring 使用者也會有同感。
我在這裡提供一個快速介紹。還有很多內容要展示,我會在後續帖子中繼續。不過,就目前而言——這應該能為您提供足夠的資訊,讓您開始使用一些真正基於 POJO 的非同步 JMS!我希望您和我一樣對此感到興奮 ;)
您需要在類路徑中包含以下 JAR 檔案。我還列出了我正在使用的版本(任何 spring-2.x 版本都應該可以。事實上,我大約在 2 分鐘前才將 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://:61616"/>
</bean>
</beans>
正如您所見,我將在 TCP 上執行 ActiveMQ(我只是從發行版的 bin 目錄中執行“activemq”)。也可以嵌入式執行(使用“vm://”),或者您可以執行 org.activemq.broker.impl.Main 類的 main 方法。如果您想獲取發行版,請訪問:http://www.activemq.org。
我在這裡故意保持簡單——主要目標是演示各個部分是如何組合在一起的。但我最想指出的一件事是,我“域”中的這些類是 POJO。您將完全看不到 Spring 或 JMS 的依賴項。
最終,我們將接受使用者的輸入(透過 stdin 的“name”),並將其轉換為某個未指定的事件的“註冊請求”。訊息將非同步傳送,但我們將有另一個佇列來處理回覆。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);
}
}
正如您所見,這只是提供一個示例。實際上,註冊對映可能會做一些事情。另外,您可以看到 20% 的註冊嘗試將被拒絕(給定一個 -1 的 confirmationId)——這不是處理註冊請求的實用方法,但它會為回覆訊息提供一些變化。同樣,重要的是這個服務類與 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 定義檔案中,請注意實際接收訊息並啟用非同步 監聽器 使用的“container”。容器需要知道它從中接收訊息的 connectionFactory 和 destination。有多種型別的容器可用,但這超出了本部落格的範圍。有關更多資訊,請閱讀參考文件:訊息監聽器容器。
在這種情況下,“listener”是 Spring 的 MessageListenerAdapter 的例項。它有一個指向 delegate(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);
}
}
}