7.4 使用Spring编纂JMS客户端

7.4 使用Spring编写JMS客户端

7.4 Writing JMS clients using Spring

7.4 使用Spring编写JMS客户端

 

ActiveMQ uses the Spring Framework to ease the various aspects of client-to-broker

communication, but the Spring Framework goes much further, with its API and container

designed specifically for JMS messaging. Together, ActiveMQ and Spring make

an excellent JMS development platform, making many common tasks extremely easy

to accomplish. Some of the tasks to be covered in this section include

 

ActiveMQ使用Spring框架来屏蔽客户端和代理之间通信的各种细节,但是Spring框架做的更好,Spring使用其

API和专为ActiveMQ进行特殊设计的JMS消息系统.ActiveMQ和Spring创造了卓越的JMS开发平台,可以轻松完成

大多数的普通任务.本章节中包含的JMS开发内容包括:

 

Configuring JMS connections—ActiveMQ provides classes that can be used to

configure URLs and other parameters of connections to brokers. The connection

factory could later be used by your application to get the appropriate

connection.

配置JMS连接 -- ActiveMQ提供了可以用来配置代理的RUL以及其他参数的类.而后你在程序中使用JMS的

连接工厂可以使用配种的适当的连接URL和其他连接参数来获取JMS连接.

 

Configuring JMS destinations—ActiveMQ destination objects can be configured

simply as beans representing JMS destinations used by your producers and

consumers.

配置JMS消息目的地 -- 可以配置一个普通的bean来代表JMS消息目的地, 以供消息生产者和消费者使用.

 

Defining JMS consumers—Spring provides helper classes that allow you to easily

configure a message listener container and hook message listeners to it.

定义JMS消息消费者--Spring提供的辅助类,允许您轻松地配置消息侦听容器和设置消息监听器钩子.

 

Implementing JMS producers—Spring also provides helper bean classes for creating

new producers.

实现JMS生产者 -- Spring通样也提供了辅助的bean用来创建新的消息生产者.

 

In the following sections, these tasks will be demonstrated and the portfolio application

will be changed to use all benefits of the ActiveMQ and Spring integration.

接下来的章节将介绍上文所述的任务,portfolio实例也会被修改以便利用ActiveMQ与Spring结合后的各种

好处.

 

7.4.1 Configuring JMS connections

7.4.1 配置JMS连接

 

As seen in the previous examples, the first step in creating a JMS application is to create

a connection to the ActiveMQ broker. The ActiveMQConnectionFactory is a factory

that creates an ActiveMQConnection, both of which can be easily used with

Spring. The following snippet shows how to define an ActiveMQConnectionFactory

using a Spring XML config:

从前面的实例代码中可以看到,创建JMS程序的首要步骤就是创建一个到ActiveMQ代理的连接.

ActiveMQConnectionFactory类是创建连接ActiveMQConnection的工厂类,在Spring中可以

很容易的使用它们.下面的的代码片段展示了如何在Spring的XML配置文件中配置

ActiveMQConnectionFactory:

 

<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">

  <property name="brokerURL" value="tcp://localhost:61616" />

  <property name="userName" value="admin" />

  <property name="password" value="password" />

</bean>

 

In the snippet, note the properties that are configured on the ActiveMQConnection-

Factory.

注意上面代码中对ActiveMQConnectionFactory属性的配置.

 

In some use cases a pool of connections is necessary in order to achieve a desired

performance. For this purpose, ActiveMQ provides the PooledConnectionFactory

class, which maintains a pool of JMS connections and sessions. Here’s an example

Spring XML configuration for the PooledConnectionFactory:

在一些实例中,为了达到满足条件的性能需要使用连接池.为此,ActiveMQ提供了

PooledConnectionFactory(池化连接工厂),该类维护一个池化的JMS连接和池化的session资源.

下面是Spring的XML文件中配置PooledConnectionFactory的代码:

 

<bean id="pooledJmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">

  <property name="connectionFactory" ref="jmsConnectionFactory" />

</bean>

 

Only one property is configured on the PooledConnectionFactory in this case—the

connectionFactory property. The connectionFactory property is used to define the

underlying connection factory to the ActiveMQ broker that’ll be used by the pooled

connection factory. In this case we’ve used our previously defined jmsConnection-

Factory bean.

在上面的配置中PooledConnectionFactory类只有一个connectionFactory属性.该属性用来

定义配置连接池中将要使用的连接到ActiveMQ代理的连接工厂.这里我们使用了前面定义的

jmsConnectionFactory bean.

 

Since the pooled connection factory has a dependency on the Apache Commons

Pool project (http://mng.bz/j3PV), you’ll need to add the JAR to the classpath. Or, if

you use Maven for your project management, just add the following dependency to

the pom.xml file:

因为池化连接工厂需要依赖于Apache的Commons Pool工程(http://mng.bz/j3PV),所以你需要添加

相关的Jar包到classpath指定的目录中.或者,如果你正在使用Maven来管理你的工程,则只需要将下面

的依赖配置加入到pom.xml文件中.

 

<dependency>

  <groupId>commons-pool</groupId>

  <artifactId>commons-pool</artifactId>

  <version>1.4</version>

</dependency>

 

The preceding XML defines a Maven dependency on the commons-pool-1.4.jar file,

and even fetches it for you automatically.

上面的XML片段定义了一个Maven对commons-pool-1.4.jar文件的依赖,并且可以自动获取

这个文件.

 

Once the JMS connection has been defined, you can move on to defining the JMS

destinations, producers, and consumers.

JMS连接定义好了以后,你就可以继续定义JMS 消息目的地,消息生产者和消息消费者了.

 

7.4.2 Configuring JMS destinations

7.4.2 配置JMS消息目的地

 

JMS destinations can be predefined in the activemq.xml file using the ActiveMQTopic

and ActiveMQQueue classes. The following snippet contains two new topic definitions

to be used in the portfolio example:

可以在activemq.xml文件中使用ActiveMQTopic和ActiveMQQueue类来预定义JMS消息目的地.

下面的代码片段定义了两个新的消息主题,这两个主题将用于portfolio实例:

 

<bean id="cscoDest" class="org.apache.activemq.command.ActiveMQTopic">

  <constructor-arg value="STOCKS.CSCO" />

</bean>

 

<bean id="orclDest" class="org.apache.activemq.command.ActiveMQTopic">

  <constructor-arg value="STOCKS.ORCL" />

</bean>

 

As you can see, these classes use constructor injection for setting a desired destination

name on the ActiveMQTopic class. Predefining topics isn’t required in ActiveMQ, but

it can be handy for environments where the broker requires clients to authenticate for

various operations. For more information about client authentication, see chapter 6.

Now that a connection and a couple of destinations exist, you can begin sending and

receiving messages.

正如你所看到的,上面配置中的类使用构造器注入为ActiveMQTopic类设置目的地名称.ActiveMQ并不

要求预定义消息主题,但是当代理需要对客户端的各种操作进行认证时,使用预定义消息主题后可以

带来一些便利.

 

7.4.3 Creating JMS consumers

7.4.3 创建JMS消息消费者

 

The next two sections touch upon basic use of Spring JMS (http://mng.bz/I0Pe) for

creating consumers and producers, as it makes creating JMS consumers and producers

incredibly easy. Although Spring JMS provides some powerful features, these two

sections won’t dive into deep details, since this is outside of the scope of this book.

Instead, we’ll show some of the basic concepts to get you up and running quickly with

the portfolio example. For more information on Spring JMS, consult the Spring

documentation.

 

下面的两个小节涉及到Spring JMS(http://mng.bz/I0Pe)的基础:创建消息消费者和消息生产者,

使用Spring JMS创建消息消费者和消息生产者也变得十分容易.尽管Spring JMS提供了一些强大

的功能,但是接下来的两个小节中不打算深入这些功能的细节,因为这超出了本书的范围.

相反,我们会介绍一些基本的概念让你能够尽快上手并运行portfolio示例程序.有关Spring JMS

的更多内容可以查阅Spring文档.

 

 

The basic abstraction for receiving messages in Spring is the message listener

container (MLC: see http://mng.bz/LJti). The MLC design provides an intermediary

between your message listener and broker to handle connections, threading, and

more, leaving you to worry only about your business logic that lives in the listener. In

the following listing, the portfolio message listener from chapter 3 is used by two

message listener containers for the two destinations that were defined in the previous

section.

 

Spring中接收消息的基本抽象机制是使用消息监听器容器(MLC: 参阅 http://mng.bz/LJti).

MLC是在消息监听器和代理之间设计的中间件,用来处理连接,线程等等,让开发者只需要关注

消息监听器中具体的业务逻辑.下面的代码清单为前面定义的两个消息目的地配置了两个监听器

容器,这两个容器都使用了第3章中定义的消息监听器.

 

Listing 7.22 Defining two Spring message listener containers and a message listener

代码清单7.22 定义两个Spring消息监听器容器和一个消息监听器

 

<!-- The message listener -->

<bean id="portfolioListener" class="org.apache.activemq.book.ch3.portfolio.Listener">

</bean>

 

<!-- Spring DMLC -->

<bean id="cscoConsumer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">

  <property name="connectionFactory" ref="jmsConnectionFactory" />

  <property name="destination" ref="cscoDest" />

  <property name="messageListener" ref="portfolioListener" />

</bean>

 

<!-- Spring DMLC -->

<bean id="orclConsumer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">

  <property name="connectionFactory" ref="jmsConnectionFactory" />

  <property name="destination" ref="orclDest" />

  <property name="messageListener" ref="portfolioListener" />

</bean>

 

Each MLC instance in listing 7.22 requires a connection factory, a destination, and a

message listener. So all you have to do is to implement a message listener bean and

leave everything else to the Spring MLC. Note that in this example we’ve used the

plain (not pooled) connection factory. This is because no connection pooling is

needed for this simple example. This example uses the Spring DefaultMessage-

ListenerContainer (DMLC), which is the most commonly used MLC. Although

numerous other properties on the DMLC can be configured, this example is using

only the basics. When these two DMLC instances start up, they’ll be ready to receive

messages and hand them off to the message listener.

 

Now let’s send some messages to ActiveMQ.

 

代码清单7.22中的每个MLC实例都需要一个连接工厂(connection factory),一个消息目的地(destination)

和一个消息监听器(message listener).所以开发者需要做的就是实现一个消息监听器bean,剩下的工作

交给Spring MLC就可以了.注意上面的配置例子中我们使用了一个普通的连接工厂(非池化连接工厂),因为

的例子比较简单,没有必要使用池化连接工厂.本例中使用的Spring中的DefaultMessageListenerContainer

(DMLC),这也是Spring MLC中最常用的消息监听器容器.DMLC中可配置的属性有很多,但本例仅使用了最基本的属性.

当这两个DMLC实例启动后,他们即可接收并使用监听器处理消息了.

 

下面让我给ActiveMQ 发一些消息.

 

7.4.4 Creating JMS producers

7.4.4 创建JMS消息

 

As was the case for receiving messages, Spring also provides conveniences for sending

messages. The crucial abstraction for sending messages is the Spring JmsTemplate

class. The JmsTemplate follows the standard template pattern to provide a convenience

class for sending messages.

 

同接收消息一样,Spring同样为发送消息提供了便利.Spring中发送消息的基本机制是使用JmsTemplate类.

JmsTemplate使用标准的模板模式为发送消息提供一个很方便的类.

 

One of the most common ways to send a message using Spring is by implementing

the Spring MessageCreator interface and utilizing it with the appropriate send()

method of the JmsTemplate class. The following listing demonstrates this by implementing

all message creation logic borrowing the stock portfolio publisher from

chapter 3.

 

使用Spring发送消息的一个常用方法是实现Spring的MessageCreator接口然后使用JmsTemplate类中合适

的send()方法.下面的代码清单借用第3张中的portfolio实例的publisher类来做说明.

 

Listing 7.23 Implementation of a MessageCreator for sending messages using Spring

代码清单7.23 使用Spring,通过实现MessageCreator来发送消息

 

public class StockMessageCreator implements MessageCreator 

{

  private int MAX_DELTA_PERCENT = 1;

  private Map<Destination, Double> LAST_PRICES = new Hashtable<Destination, Double>();

  Destination stock;

 

  public StockMessageCreator(Destination stock) 

  {

    this.stock = stock;

  }

  

  public Message createMessage(Session session) throws JMSException 

  {

    Double value = LAST_PRICES.get(stock);

    if (value == null) 

    {

      value = new Double(Math.random() * 100);

    }

    

    // lets mutate the value by some percentage

    double oldPrice = value.doubleValue();

    value = new Double(mutatePrice(oldPrice));

    LAST_PRICES.put(stock, value);

    double price = value.doubleValue();

    double offer = price * 1.001;

    boolean up = (price > oldPrice);

    MapMessage message = session.createMapMessage();

    message.setString("stock", stock.toString());

    message.setDouble("price", price);

    message.setDouble("offer", offer);

    message.setBoolean("up", up);

    System.out.println("Sending: " + ((ActiveMQMapMessage)message).getContentMap() + " on destination: " + stock);

    return message;

  }

  

  protected double mutatePrice(double price) 

  {

    double percentChange = (2 * Math.random() * MAX_DELTA_PERCENT) - MAX_DELTA_PERCENT;

    return price * (100 + percentChange) / 100;

  }

}

 

The MessageCreator interface defines only the createMessage() method, which

returns a JMS message. Here, we’ve implemented some logic for creating random

stock prices, and we’re creating an appropriate JMS map message to hold all of the relevant

data. To send the message, the JmsTemplate’s send() method will utilize the

StockMessageCreator as shown next.

 

MessageCreator接口只定义了createMessage()方法,该方法返回一个JMS消息对象.这里,我们实现了一些逻辑来

创建随机的股票价格然后创建了一个合适的JMS map消息来保存这些相关数据.JmsTemplate的send()方法利用

StockMessageCreator来发送消息.

 

Listing 7.24 JMS publisher implementation in Spring

代码清单7.24 使用Spring 实现JMS publisher

 

public class SpringPublisher 

{

  private JmsTemplate template;

  private int count = 10;

  private int total;

  private Destination[] destinations;

  private HashMap<Destination,StockMessageCreator> creators = new HashMap<Destination,StockMessageCreator>();

  

  public void start() 

  {

    while (total < 1000) 

    {

      for (int i = 0; i < count; i++) 

      {

        sendMessage();

      }

      total += count;

      System.out.println("Published '" + count + "' of '" + total + "' price messages");

      try 

      {

        Thread.sleep(1000);

      } 

      catch (InterruptedException x) 

      {}

      }

    }

    

    protected void sendMessage() {

    int idx = 0;

    while (true) 

    {

      idx = (int)Math.round(destinations.length * Math.random());

      if (idx < destinations.length) 

      {

        break;

      }

    }

    Destination destination = destinations[idx];

    template.send(destination, getStockMessageCreator(destination));

  }

  

  private StockMessageCreator getStockMessageCreator(Destination dest) 

  {

    if (creators.containsKey(dest)) 

    {

      return creators.get(dest);

    } 

    else 

    {

      StockMessageCreator creator = new StockMessageCreator(dest);

      creators.put(dest, creator);

      return creator;

    }

  }

  

  // getters and setters goes here

}

 

The important thing to note in listing 7.24 is how the send() method uses the message

creator. Everything else in this example is the same as in the original stock portfolio

publisher from chapter 3. Now you have all the necessary components to publish

messages to ActiveMQ using Spring. All that’s left to be done is to configure it properly

as demonstrated in the following listing.

 

代码清单7.24中需要重点关注的是send()方法是如何使用消息创建者(message creator)的.本例

中其他部分都和第3章中原来的stock portfolio例子中的publisher相同.现在,使用Spring

发送消息到ActiveMQ所需的所有组件都有了,剩下的就是配置Spring了,如下面代码所示:

 

Listing 7.25 JMS publisher configuration in Spring

代码清单7.25 在Spring中配置 JMS publisher

 

<!-- Spring JMS Template -->

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">

  <property name="connectionFactory" ref="pooledJmsConnectionFactory" />

</bean>

 

<bean id="stockPublisher" class="org.apache.activemq.book.ch7.spring.SpringPublisher">

  <property name="template" ref="jmsTemplate" />

  <property name="destinations">

    <list>

      <ref local="cscoDest" />

      <ref local="orclDest" />

    </list>

  </property>

</bean>

 

The snippet in listing 7.25 shows an instance of the Spring JmsTemplate and the publisher.

The publisher simply needs a reference to the JMS destinations being used, and

the JmsTemplate requires a connection factory.

 

上述代码清单7.25配置了Spring JmsTemplate 和 publisher的实例.publisher需要使用一个JMS消息

目的地对象的引用,JmsTemplate需要一个连接工厂.

 

NOTE The pooled connection factory is used with the JmsTemplate. This is

important because the JmsTemplate is designed for use with Java EE containers

in mind, which typically provide connection pooling capabilities as

required by the Java EE specifications. Every call to the JmsTemplate.send()

method creates and destroys all the JMS resources (connections, consumers,

and producers). So if you’re not using a Java EE container, make sure to use a

pooled connection factory for sending messages with the JmsTemplate.

 

注意JmsTemplate使用了一个池化的连接工厂.这点很重要,因为JmsTemplate是专门设计成使用

Java EE容器的,而Java EE规范要求提供池化连接.每次调用JmsTemplate的send()方法都会创建

和销毁JMS资源(连接,消费者和生产者).因此,如果你没有使用Java EE容器,请确保在利用

JmsTemplate发送消息时使用的是池化的连接工厂.

 

The connections and destinations are defined; the consumers and producer have

been created. Now let’s run the example.

 

至此,连接和消息目的地已经定义完毕,消息消费者和消息生产者也已经准备就绪.

下面让我来运行这个例子.

 

7.4.5 Putting it all together

7.4.5 整合

 

After implementing all pieces of the example, the application should be ready to run.

Take a look at the following listing to see the main method that will execute the

example.

 

实现了这个例子所需的所有组件后,就可以运行程序了.下面的代码清单中main将运行这个示例程序.

 

Listing 7.26 The main method for the Spring example

代码清单7.26 Spring 消息客户端实例中的main方法

 

public class SpringClient 

{

  public static void main(String[] args) 

  {

    BrokerService broker = new BrokerService();

    broker.addConnector("tcp://localhost:61616");

    broker.setPersistent(false);

    broker.start();

    FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext

    ( "src/main/resources/org/apache/activemq/book/ch7/spring-client.xml");

    SpringPublisher publisher = (SpringPublisher)context.getBean("stockPublisher");

    publisher.start();

  }

}

 

This simple class starts a minimal ActiveMQ broker configuration and initializes the

Spring application context to start the JMS clients.

 

这个类启动了一个最小化配置的ActiveMQ代理,并初始化了Spring的应用程序上下文对象然后启动

了JMS客户端.

 

The example can be run from the command line using the following command.

可以使用下面的命令来运行示例程序.

 

Listing 7.27 Run the Spring example

代码清单7.27 运行SpringJMS客户端示例程序

 

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch7.spring.SpringClient -Dlog4j.configuration=file:src/main/java/log4j.properties

 

...

Sending: {price=65.958996694, stock=CSCO, offer=66.0249556914, up=false}

on destination: topic://STOCKS.CSCO topic://STOCKS.IONA 79.97 80.05 down

Sending: {price=80.67595675108, stock=ORCL, offer=80.7566327078, up=true}

on destination: topic://STOCKS.ORCL topic://STOCKS.JAVA 65.96 66.02 down

Sending: {price=65.63333898492, stock=CSCO, offer=65.69897232391, up=false}

on destination: topic://STOCKS.CSCO topic://STOCKS.IONA 80.68 80.76 up

Sending: {price=80.50525969261, stock=ORCL, offer=80.58576495231, up=false}

on destination: topic://STOCKS.ORCL topic://STOCKS.JAVA 65.63 65.70 down

Sending: {price=81.2186806051, stock=ORCL, offer=81.29989928577, up=true}

on destination: topic://STOCKS.ORCL topic://STOCKS.IONA 80.51 80.59 down

Sending: {price=65.48960846536, stock=CSCO, offer=65.5550980738, up=false}

on destination: topic://CSCO topic://STOCKS.IONA 81.22 81.30 up

topic://STOCKS.JAVA 65.49 65.56 down

...

 

As you can see, both producer and consumer print their messages to standard output

as the example runs.

 

你可以看到消息生产者和消费者都在标准输出中打印了各自的消息.

 

In this section, you used Spring to augment the stock portfolio example application

from chapter 3. You were able to reuse most of the original logic, but this time

you used some Spring utilities to simplify the example a lot. As stated previously, this

example simply touched on the basics of using Spring JMS. If you’d like more information

about Spring JMS, see the documentation (http://mng.bz/I0Pe).

 

本节中,使用Spring JMS扩展了第3章中的stock portfolio实例.原来代码中大部分逻辑都可以重用,但这次

使用了一些Spring框架里的内容来大大简化这个实例.正如前面提到过的,这里例子中只是简单额介绍了

Spring JMS的一些基本概念.如果你打算了解更多的关于Spring JMS相关信息,请查阅文档

(http://mng.bz/I0Pe).