当前位置: 首页 > ActiveMQ in Action 读书笔记 > 正文

7.4 使用Spring编写 JMS 客户端

7.4 Writing JMS clients using Spring

ActiveMQ uses the Spring Framework to ease the various aspects of client-to-brokercommunication, but the Spring Framework goes much further, with its API and containerdesigned specifically for JMS messaging. Together, ActiveMQ and Spring makean excellent JMS development platform, making many common tasks extremely easyto accomplish.

Some of the tasks to be covered in this section include

7.4 使用Spring编写JMS客户端

ActiveMQ使用Spring框架来屏蔽客户端和代理之间通信的各种细节,但是Spring框架做的更好,Spring使用其API和专为ActiveMQ进行特殊设计的JMS消息系统.ActiveMQ和Spring创造了卓越的JMS开发平台,可以轻松完成大多数的普通任务.

本章节中包含的JMS开发内容包括:

Configuring JMS connections—ActiveMQ provides classes that can be used toconfigure URLs and other parameters of connections to brokers. The connectionfactory could later be used by your application to get the appropriateconnection.

配置JMS连接 — ActiveMQ提供了可以用来配置代理的RUL以及其他参数的类.而后你在程序中使用JMS的连接工厂可以使用配种的适当的连接URL和其他连接参数来获取JMS连接.

Configuring JMS destinations—ActiveMQ destination objects can be configuredsimply as beans representing JMS destinations used by your producers andconsumers.

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

Defining JMS consumers—Spring provides helper classes that allow you to easilyconfigure 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 applicationwill be changed to use all benefits of the ActiveMQ and Spring integration.

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

7.4.1 Configuring JMS connections

As seen in the previous examples, the first step in creating a JMS application is to createa connection to the ActiveMQ broker. The ActiveMQConnectionFactory is a factorythat creates an ActiveMQConnection, both of which can be easily used withSpring. The following snippet shows how to define an ActiveMQConnectionFactoryusing a Spring XML config:

7.4.1 配置JMS连接

从前面的实例代码中可以看到,创建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.

In some use cases a pool of connections is necessary in order to achieve a desiredperformance. For this purpose, ActiveMQ provides the PooledConnectionFactoryclass, which maintains a pool of JMS connections and sessions. Here’s an exampleSpring XML configuration for the PooledConnectionFactory:

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

在一些实例中,为了达到满足条件的性能需要使用连接池.为此,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—theconnectionFactory property. The connectionFactory property is used to define theunderlying connection factory to the ActiveMQ broker that’ll be used by the pooledconnection 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 CommonsPool project (http://mng.bz/j3PV), you’ll need to add the JAR to the classpath. Or, ifyou use Maven for your project management, just add the following dependency tothe 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文件的依赖,并且可以自动获取
这个文件.

 译注:5.8.0版本ActiveMQ文件 org.apache.activemq.pool.PooledConnectionFactory”类来自activemq-pool.jar 还要添加依赖  

<dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-pool</artifactId>
      <version>5.8.0</version>
    </dependency>

 不需要 commons-pool 这个依赖了

Once the JMS connection has been defined, you can move on to defining the JMSdestinations, producers, and consumers.

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

7.4.2 Configuring JMS destinations

JMS destinations can be predefined in the activemq.xml file using the ActiveMQTopicand ActiveMQQueue classes. The following snippet contains two new topic definitionsto be used in the portfolio example:

7.4.2 配置JMS消息目的地

可以在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 destinationname on the ActiveMQTopic class. Predefining topics isn’t required in ActiveMQ, butit can be handy for environments where the broker requires clients to authenticate forvarious operations. For more information about client authentication, see chapter 6.Now that a connection and a couple of destinations exist, you can begin sending andreceiving messages.

正如你所看到的,上面配置中的类使用构造器注入为ActiveMQTopic类设置目的地名称.ActiveMQ并不要求预定义消息主题,但是当代理需要对客户端的各种操作进行认证时,使用预定义消息主题后可以带来一些便利.

7.4.3 Creating JMS consumers

The next two sections touch upon basic use of Spring JMS (http://mng.bz/I0Pe) forcreating consumers and producers, as it makes creating JMS consumers and producersincredibly easy. Although Spring JMS provides some powerful features, these twosections 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 withthe portfolio example. For more information on Spring JMS, consult the Springdocumentation.

7.4.3 创建JMS消息消费者

下面的两个小节涉及到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 listenercontainer (MLC: see http://mng.bz/LJti). The MLC design provides an intermediarybetween your message listener and broker to handle connections, threading, andmore, leaving you to worry only about your business logic that lives in the listener. Inthe following listing, the portfolio message listener from chapter 3 is used by twomessage listener containers for the two destinations that were defined in the previoussection.

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

Spring中接收消息的基本抽象机制是使用消息监听器容器(MLC: 参阅 http://mng.bz/LJti).MLC是在消息监听器和代理之间设计的中间件,用来处理连接,线程等等,让开发者只需要关注消息监听器中具体的业务逻辑.下面的代码清单为前面定义的两个消息目的地配置了两个监听器容器,这两个容器都使用了第3章中定义的消息监听器.

代码清单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 amessage listener. So all you have to do is to implement a message listener bean andleave everything else to the Spring MLC. Note that in this example we’ve used theplain (not pooled) connection factory. This is because no connection pooling isneeded for this simple example. This example uses the Spring DefaultMessage-ListenerContainer (DMLC), which is the most commonly used MLC. Althoughnumerous other properties on the DMLC can be configured, this example is usingonly the basics. When these two DMLC instances start up, they’ll be ready to receivemessages 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

As was the case for receiving messages, Spring also provides conveniences for sendingmessages. The crucial abstraction for sending messages is the Spring JmsTemplateclass. The JmsTemplate follows the standard template pattern to provide a convenienceclass for sending messages.

7.4.4 创建JMS消息

同接收消息一样,Spring同样为发送消息提供了便利.Spring中发送消息的基本机制是使用JmsTemplate类.JmsTemplate使用标准的模板模式为发送消息提供一个很方便的类.

One of the most common ways to send a message using Spring is by implementingthe Spring MessageCreator interface and utilizing it with the appropriate send()method of the JmsTemplate class. The following listing demonstrates this by implementingall message creation logic borrowing the stock portfolio publisher fromchapter 3.
Listing 7.23 Implementation of a MessageCreator for sending messages using Spring

使用Spring发送消息的一个常用方法是实现Spring的MessageCreator接口然后使用JmsTemplate类中合适的send()方法.下面的代码清单借用第3章中的portfolio实例的publisher类来做说明.
代码清单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, whichreturns a JMS message. Here, we’ve implemented some logic for creating randomstock prices, and we’re creating an appropriate JMS map message to hold all of the relevantdata. To send the message, the JmsTemplate’s send() method will utilize theStockMessageCreator as shown next.
Listing 7.24 JMS publisher implementation in Spring

MessageCreator接口只定义了createMessage()方法,该方法返回一个JMS消息对象.这里,我们实现了一些逻辑来创建随机的股票价格然后创建了一个合适的JMS map消息来保存这些相关数据.JmsTemplate的send()方法利用StockMessageCreator来发送消息.
代码清单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 messagecreator. Everything else in this example is the same as in the original stock portfoliopublisher from chapter 3. Now you have all the necessary components to publishmessages to ActiveMQ using Spring. All that’s left to be done is to configure it properlyas demonstrated in the following listing.
Listing 7.25 JMS publisher configuration in Spring

代码清单7.24中需要重点关注的是send()方法是如何使用消息创建者(message creator)的.本例中其他部分都和第3章中原来的stock portfolio例子中的publisher相同.现在,使用Spring发送消息到ActiveMQ所需的所有组件都有了,剩下的就是配置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, andthe JmsTemplate requires a connection factory.

上述代码清单7.25配置了Spring JmsTemplate 和 publisher的实例.publisher需要使用一个JMS消息目的地对象的引用,JmsTemplate需要一个连接工厂.

NOTE The pooled connection factory is used with the JmsTemplate. This isimportant because the JmsTemplate is designed for use with Java EE containersin mind, which typically provide connection pooling capabilities asrequired 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 apooled 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 havebeen created. Now let’s run the example.

至此,连接和消息目的地已经定义完毕,消息消费者和消息生产者也已经准备就绪.下面让我来运行这个例子.

7.4.5 Putting it all together

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 theexample.
Listing 7.26 The main method for the Spring example

7.4.5 整合 

实现了这个例子所需的所有组件后,就可以运行程序了.下面的代码清单中main将运行这个示例程序.
代码清单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.
The example can be run from the command line using the following command.
Listing 7.27 Run the Spring example

这个类启动了一个最小化配置的ActiveMQ代理,并初始化了Spring的应用程序上下文对象然后启动了JMS客户端.
可以使用下面的命令来运行示例程序.
代码清单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 applicationfrom chapter 3. You were able to reuse most of the original logic, but this timeyou used some Spring utilities to simplify the example a lot. As stated previously, thisexample simply touched on the basics of using Spring JMS. If you’d like more informationabout Spring JMS, see the documentation (http://mng.bz/I0Pe).

本节中,使用Spring JMS扩展了第3章中的stock portfolio实例.原来代码中大部分逻辑都可以重用,但这次使用了一些Spring框架里的内容来大大简化这个实例.正如前面提到过的,这里例子中只是简单额介绍了Spring JMS的一些基本概念.如果你打算了解更多的关于Spring JMS相关信息,请查阅文档(http://mng.bz/I0Pe).

赞 赏

   微信赞赏  支付宝赞赏


本文固定链接: https://www.jack-yin.com/coding/translation/activemq-in-action/1568.html | 边城网事

该日志由 边城网事 于2013年11月13日发表在 ActiveMQ in Action 读书笔记 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: 7.4 使用Spring编写 JMS 客户端 | 边城网事
关键字: , , ,

7.4 使用Spring编写 JMS 客户端 暂无评论

发表评论

快捷键:Ctrl+Enter