`

用Spring JMS使异步消息变得简单[多图]

阅读更多

异步处理通信是面向服务架构(SOA)的重要部分,因为企业中的许多系统通信,尤其是跟外部系统通信本来就是异步的。Java消息服务(JMS)就是用来编写异步消息J2EE应用的API。使用JMS API的传统消息实现涉及到象这样的一些步骤:查找对列连接工厂、队列资源以及在实际发送和接受消息前,创建JMS会话(JMS session)。

  Spring framework简化了用JEE组件(包括JMS)开发JMS应用的工作。它提供了一个模板机制来隐藏典型的JMS实现细节,所以开发者可以专注于消息处理任务而不用担心怎样创建、访问和释放JMS资源。

  本文用一个运行在JBoss MQ server上的简单Web应用概述了Spring JMS API和怎样使用它异步处理(发送和接受)消息。我将对比JMS实现的传统方法和Spring JMS实现方法,以显示使用Spring JMS来处理消息是多么的简单和灵活。

  异步消息和SOA

  现实世界中,大多数Web请求是同步处理的。例如,当用户登陆一个站点,他或她输入用户名和口令以及服务器识别登陆凭证。如果身份验证成功,程序让用户进入站点。这里,登陆请求从客户端被接受后,立即被处理。信用卡授权也是一个同步处理的例子;仅当服务器核实了发送进来的信用卡号是有效并且该客户的帐号有足够的信用额度后,才允许客户继续进行下一步动作。让我们来考察一下订单处理系统中的支付结算步骤。一旦系统核实了那个用户的信用卡信息是正确的,而且帐户上有足够的资金,那么不需要等到支付细节和转帐最终完成。支付结算用异步方式处理,如此客户便可以继续进行结帐处理。

  与典型的同步请求相比,异步处理用于需要长时间来处理的请求。异步处理的另外一个例子是住房贷款处理应用中,处理提交到AUS(Automated Underwriting System)的贷款请求。贷款人提交贷款申请后,抵押公司发送请求到AUS取得信用历史信息。因为该请求要取得综合详细的信用报告如贷款人当前和过去的信用帐户,最近的支付以及其它金融详细信息,所以从请求到获得响应常常需要很长时间。对客户端程序来说开一个到服务器的连接并且长时间等待响应是没有意义的。于是就有了异步通信;也就是,一旦请求被提交,它就被放入队列里面并且客户断开服务器连接。然后,AUS服务从特定队列摘取请求,处理它,把结果消息放入另外一个消息队列。最后客户程序从消息队列摘取响应结果继续处理信用历史结果信息。

JMS

  如果用过JMS的话,会发现它类似写JDBC或JCA代码。它有创建或检索JMS资源的样板代码,每当你需要编写一个新类来发送或接受消息时,都得重复编写那个样本代码。下面列出了传统JMS实现涉及的步骤:

  1、创建JNDI初始上下文context;

  2、从JNDI上下文获得队列连接工厂;

  3、从队列连接工厂取得队列Queue;

  4、创建一个Session对象;

  5、创建一个发送或接受对象;

  6、利用第5部创建的发送或接受对象发送或接受消息;

  7、处理完消息后,关闭所有JMS资源。

  如你所见,只有第6步是处理消息的步骤。其他步骤都只是管理JMS资源,与实际业务需求无关,但开发者不得不编写和维护那些附加步骤代码。

  Spring JMS

  Spring框架提供一个模板机制来隐藏Java API细节。JEE开发者可用JDBCTemplate 和JNDITemplate类来分别访问后端数据库和JEE资源(数据源,连接池)。JMS没有异常。Spring提供了JMSTemplate类,所以开发者不必为JMS实现编写样本代码。当开发JMS应用时,Spring提供了一下一些优势:

  1、提供了一个JMS的抽象API,简化了JMS的使用。如:访问目的地(队列或主体)和出版消息到特定目的地。

  2、JEE开发者不必关心JMS不同版本之间的差异(如JMS 1.0.2 同 JMS 1.1);

  3、开发者不必特定地处理JMS异常,因为Spring为JMS代码抛出的任何JMS异常提供了一个unchecked异常。

  一旦你在JMS应用中开始使用Spring,你将会欣赏到异步消息处理的简易性。Spring JMS框架提供了各种java类使JMS开发变得简单。

  

  表1,Spring JMS类

 

随后的部分,我将详细解释表1中的类(如JmsTemplate, DestinationResolver,和 MessageConverter)。    JMSTemplate

  JmsTemplate提供各种方法来接收和发送消息。表2是方法列表。

  

  表2。JMS模板方法

  使用JNDI上下文存储和检索目的地。当配置Spring应用上下文时,我们用JndiObjectFactoryBean获得JMS目的地引用。DestinationResolver用来解析目的地名称到一个JMS目的地,当应用有许多目的地时,那是很有帮助的。DynamicDestinationResolver(缺省DestinationResolver实现)用于解析动态目的地。

  MessageConverter接口定义了java对象和JMS消息之间转换的契约。使用转换器,应用代码可以专注于业务对象,不用操心它是如何代表JMS消息的。SimpleMessageConverter(和SimpleMessageConverter102)是缺省MessageConverter实现。它们用于将String、字节数组((byte[])、Map、Serializable对象分别转换成JMS TextMessage、JMS BytesMessage,JMS MapMessage,JMS ObjectMessage。你可以编写MessageConverter接口的定制实现并结合XML绑定框架如JAXB, Castor, Commons Digester, XMLBeans, 或 XStream来转换XML文档到TextMessage。

样本应用

  我将用一个样本贷款应用处理系统(叫LoanProc)来说明怎样在JMS应用中使用Spring。作为贷款处理的一部分,LoanProc发送贷款详细资料(loan ID, borrower name, borrower's SSN, loan expiration date, and loan amount)从AUS系统请求信贷历史。为让例子简单一点,我们将基于两个参数:信用评分和贷款数量来获得信贷历史详细资料。让我们假定处理信用检查请求的业务规则如下:

  1、如果贷款数量等于或小于$500,000,那么贷款人必须至少有一个“good”信用(例如,贷款人的信用评分在680到699之间);

  2、如果贷款数量超过$500,000,那么贷款人必须至少要有一个“very good”信用,这意味他/她的信用评分超过700。

  贷款应用Use Case

  贷款请求处理Use Case由下列步骤组成:

  1、用户在贷款申请web页面输入贷款详细资料并提交贷款申请;

  2、然后程序发送贷款详细资料到AUS系统取得信用历史详细资料。用发送请求到名叫CreditRequestSendQueue的消息队列来完成。

  3、AUS系统从队列摘取贷款详细资料并用贷款参数来从数据库检索信用历史信息;

  4、然后AUS系统用找到的贷款人信用历史信息创建一个新的消息并发送到名叫CreditRequestReceiveQueue的消息队列;

  5、最后LoanProc从接收消息队列摘取响应消息并处理贷款申请,决定申请是被核准还是拒绝。

  应用中,在同样的JBoss MQ server中配置了两个消息队列。Use Case用序列图1表示如下:

  

  图1:贷款处理应用的序列图

  技术 <!-- 分页 --><!-- 分页 -->

  表3列出了例子应用中用到的一些技术和开源框架

<!-- 分页 -->

  JmsTemplate提供了几个helper方法来执行基本操作。开始使用JmsTemplate前,有必要知道JMS提供者支持哪种JMS规范。JBoss AS 4.0.2 和 WebLogic 8.1服务器支持JMS1.0.2规范。WebLogic 服务器 9.0包含JMS1.1支持。JMS1.1统一了PTP和Pub/Sub编程接口。有了这个改变,开发者可以创建一个事务会话,然后在同一个JMS事务中,从Queue(PTP)接受消息和发送一个消息到Topic(Pub/Sub)。JMS1.1向后兼容JMS1.0,因此基于JMS1.0编写的代码仍然能跟JMS1.1工作。

  表3,JMS应用中用到的框架

  使用Hermes 的JMS资源设置

  为异步处理消息,首先,我们需要消息队列来发送和接收消息。我们在JBoss中使用xml配置文件创建消息队列并且用JMS控制台来浏览队列详细资料。清单1显示了XML配置文件JMS配置片断。(这个片断可以被添加到%JBOSS_HOME%serveralldeploy-hasingletonjms目录下的jbossmq-destinations-service.xml文件中。)

  清单1,JBoss MQ服务器中JMS队列配置

<!-- Credit Request Send Queue -->
<mbean code="org.jboss.mq.server.jmx.Queue"
 name="jboss.mq.destination:service=Queue,name=CreditRequestSendQueue">
 <depends optional-attribute-name="DestinationManager">
  jboss.mq:service=DestinationManager
 </depends>
</mbean>
<!-- Credit Request Receive Queue -->
<mbean code="org.jboss.mq.server.jmx.Queue"
 name="jboss.mq.destination:service=Queue,name=CreditRequestReceiveQueue">
 <depends optional-attribute-name="DestinationManager">
  jboss.mq:service=DestinationManager
 </depends>
</mbean>

  现在,让我们看看怎么使用一个叫做Hermes的JMS工具浏览消息队列。Hermes是一个Java Swing应用,它能创建、管理和监控JMS提供者中的JMS destination。这样的JMS提供者有JBossMQ, WebSphereMQ, ActiveMQ 和 Arjuna。从website下载Hermes并解压zip文件到一个本地目录(如c:dev oolshermes)。一旦安装,双击hermes.bat (在bin目录)启动程序。

为在Hermes中配置JBossMQ服务器,参考Hermes站点上的demo。它一步一步形象说明了JBoss MQ的配置。当配置一个新的JNDI初始上下文时,输入下面的配置信息。

  providerURL = jnp://localhost:1099

  initialContextFactory = org.jnp.interfaces.NamingContextFactory

  urlPkgPrefixes = org.jnp.interfaces:org.jboss.naming

  securityCredentials = admin

  securityPrincipal = admin

  当创建一个新destinations时,输入queue/CreditRequestSendQueue 和 queue/CreditRequestReceiveQueue。图2显示了JMS控制台主屏幕,它显示了为样本JMS应用创建的新消息。

  

  图2.Hermes中所有destinations的截屏

  图3是消息发送者发送一些消息到CreditRequestSendQueue后,Hermes JMS控制台显示的消息队列详细资料。你能看到这里队列中有5个消息并且控制台显示了消息的详细信息如message ID、message destination、time stamp、和实际的消息。

  

  图3.Hermes中队列详细资料截屏

  使用Spring JMS,异步消息变得简单

  

  表4,Spring JMS配置参数

  Spring配置

  已经有了运行样本应用所需的JMS destinations,现在该是进入用XML Spring配置文件(叫做spring-jms.xml)装配JMS组件的细节的时候了。用IOC设计模式中的setter依赖注入原理装入这些组件。让我们仔细看看组件,为每一个JMS组件显示了一个XML配置片断。

 

JNDI上下文是获取JMS资源的入口,所以我们首先配置一个JNDI模板。清单2显示了一名为jndiTemplate的Spring bean,它具有取得JNDI初始上下文必须的常用参数。

  清单2,JNDI上下文模板

<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
  <property name="environment">
    <props>
      <prop key="java.naming.factory.initial">
        org.jnp.interfaces.NamingContextFactory
      </prop>
      <prop key="java.naming.provider.url">
        localhost
      </prop>
      <prop key="java.naming.factory.url.pkgs">
        org.jnp.interfaces:org.jboss.naming
      </prop>
    </props>
  </property>
</bean>

  下一步,我们配置队列连接工厂。清单3显示了队列连接工厂。

  清单3,JMS对了连接工厂配置

<bean id="jmsQueueConnectionFactory"
   class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiTemplate">
    <ref bean="jndiTemplate"/>
  </property>
  <property name="jndiName">
    <value>UIL2ConnectionFactory</value>
  </property>
</bean>

  我们定义了两个JMS destinations来发送和接收消息。清单4和清单5显示了这些细节。

清单4,发送队列配置<bean id="sendDestination"
  class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiTemplate">
    <ref bean="jndiTemplate"/>
  </property>
  <property name="jndiName">
    <value>queue/CreditRequestSendQueue</value>
  </property>
</bean>

  清单5,接收队列配置

<bean id="receiveDestination"
  class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiTemplate">
    <ref bean="jndiTemplate"/>
  </property>
  <property name="jndiName">
    <value>queue/CreditReqeustReceiveQueue</value>
  </property>
</bean>

  然后,我们配置JmsTemplate组件。我们在样本应用中使用JmsTemplate102。使用defaultDestination属性来指定JMS destination。

  清单6,JMS template配置

<bean id="jmsTemplate"
   class="org.springframework.jms.core.JmsTemplate102">
  <property name="connectionFactory">
    <ref bean="jmsQueueConnectionFactory"/>
  </property>
  <property name="defaultDestination">
    <ref bean="destination"/>
  </property>
  <property name="receiveTimeout">
    <value>30000</value>
  </property>
</bean>

  最后,我们配置发送和接收者组件。清单7和清单8显示了Sender 和Receiver 对象配置。

清单7,JMS Sender配置Listing 7. JMS Sender configuration
<bean id="jmsSender" class="springexample.client.JMSSender">
  <property name="jmsTemplate">
    <ref bean="jmsTemplate"/>
  </property>
</bean>

  清单8,JMS Receiver配置

  <bean id="jmsReceiver" class="springexample.client.JMSReceiver">
  <property name="jmsTemplate">
    <ref bean="jmsTemplate"/>
  </property>
</bean>
  测试和监控
  我些了一个叫做LoanApplicationControllerTest的测试类来测试LoanProc应用。我们使用这个类来设置贷款参数和调用那个信用请求服务类。
  让我们看看使用传统JMS而不用Spring JMS API的消息发送者实现。清单9显示了MessageSenderJMS这个类的sendMessage方法,这个类具备使用JMS API处理消息的所有必须步骤。
  清单9,传统JMS实现
public void sendMessage() {
  queueName = "queue/CreditRequestSendQueue";
  System.out.println("Queue name is " + queueName);
  /*
   * Create JNDI Initial Context
   */
  try {
    Hashtable env = new Hashtable();
    env.put("java.naming.factory.initial",
      "org.jnp.interfaces.NamingContextFactory");
    env.put("java.naming.provider.url","localhost");
    env.put("java.naming.factory.url.pkgs",
      "org.jnp.interfaces:org.jboss.naming");
    jndiContext = new InitialContext(env);
  } catch (NamingException e) {
    System.out.println("Could not create JNDI API " +
      "context: " + e.toString());
  }
  /*
   * Get queue connection factory and queue objects from JNDI context.
   */
  try {
    queueConnectionFactory = (QueueConnectionFactory)
    jndiContext.lookup("UIL2ConnectionFactory");
    queue = (Queue) jndiContext.lookup(queueName);
  } catch (NamingException e) {
    System.out.println("JNDI API lookup failed: " +
      e.toString());
  }
  /*
   * Create connection, session, sender objects.
   * Send the message.
   * Cleanup JMS connection.
   */
  try {
    queueConnection =
      queueConnectionFactory.createQueueConnection();
    queueSession = queueConnection.createQueueSession(false,
        Session.AUTO_ACKNOWLEDGE);
    queueSender = queueSession.createSender(queue);
    message = queueSession.createTextMessage();
    message.setText("This is a sample JMS message.");
    System.out.println("Sending message: " + message.getText());
    queueSender.send(message);
  } catch (JMSException e) {
    System.out.println("Exception occurred: " + e.toString());
  } finally {
    if (queueConnection != null) {
      try {
        queueConnection.close();
      } catch (JMSException e) {}
    }
  }
}
  现在,让我们看看用Spring实现的消息发送者。清单10显示了MessageSenderSpringJMS类中send方法代码。

 

  清单10,用Spring API 的JMS实现

  public void send() {
  try {
    ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {
        "spring-jms.xml"});
    System.out.println("Classpath loaded");
    JMSSender jmsSender = (JMSSender)appContext.getBean("jmsSender");
    jmsSender.sendMesage();
    System.out.println("Message sent using Spring JMS.");
  } catch(Exception e) {
    e.printStackTrace();
  }
}
  如你所见,所有与管理JMS资源相关的资源步骤都由Spring容器用配置文件处理。我们仅需要获取JMSSender引用并且调用它上面的sendMessage即可。

  结论

  本文中,我们看到Spring框架如何简化了使用JMS API的异步消息应用开发的工作。Spring移走了所有JMS消息处理必须的样板代码,如获取连接工厂,从Java代码创建队列和会话对象并在运行时用配置文件装配它们。由于这个强大的IOC原理,我们可以不必修改Java代码便可动态交换JMS资源对象。

  因为异步消息是构成SOA框架整体所需的一部分,Spring非常适合放入SOA工具集。同样,JMS管理工具如Hermes使得创建,管理和控制JMS资源变得简单,尤其对系统管理员。

<!-- 分页 --><!-- 分页 --><!-- 分页 -->

  用在样本应用中的这些消息队列名和其它JMS以及JNDI参数如下表4所示。

分享到:
评论

相关推荐

    jms spring

    标题中的“jms spring”指的是Java消息服务(Java Message Service)与Spring框架的集成。Java消息服务(JMS)是一个应用程序接口(API),它提供了一种标准的方式,用于在不同的应用之间发送和接收消息,特别是在...

    springboot整合jms进行邮件的发送

    Spring Boot以其强大的依赖管理和自动化配置能力,使得集成各种功能变得异常简单,包括集成JMS进行邮件发送。JMS是Java平台中用于消息传递的一种标准接口,它为应用程序提供了一种与消息中间件交互的方法。 首先,...

    JMS学习笔记

    例如,结合Spring框架中的Spring JMS模块,可以轻松地在应用程序中集成消息服务,实现异步处理、任务调度等功能。同时,JMS也广泛应用于微服务架构中,作为服务间通信的桥梁。 总之,JMS是Java世界里不可或缺的通信...

    spring 2.0中文参考文档

    Spring对JMS(Java Message Service)提供了全面的支持,允许开发者实现异步通信和消息驱动的架构。 12. **国际化和本地化**: Spring提供了一套机制,帮助应用支持多语言环境,允许用户根据其首选语言获取相应的...

    Spring 3.2.0 API_CHM

    10. **消息支持**:Spring的Message模块支持消息传递,包括JMS(Java Message Service)和AMQP(Advanced Message Queuing Protocol),使得异步通信和解耦成为可能。 `spring-3.2.0api.chm`文件虽然没有内置的搜索...

    Spring3.0中文帮助文档

    6. **消息支持**:Spring 3.0提供了消息传递支持,包括JMS(Java Message Service)和AMQP(Advanced Message Queuing Protocol),方便在分布式系统中进行异步通信。 7. **JSR-303/JSR-349 Bean Validation**:...

    spring 3.0 jar 包

    9. **消息支持**:Spring 3.0加强了对JMS(Java Message Service)的支持,方便实现异步消息传递和分布式通信。 10. **国际化(i18n)和本地化(l10n)**:Spring 3.0提供了处理多语言环境的工具和类,便于开发全球...

    spring4.1源码

    虽然Spring Boot不是Spring 4.1的一部分,但4.1版本与Spring Boot的结合使得创建独立的、生产级别的基于Spring的应用变得更加简单。Spring Boot默认配置了很多常见的设置,减少了大量配置工作。 9. **性能优化** ...

    Spring基本架包

    9. **spring-jms-4.3.18.RELEASE.jar**:这个模块提供了JMS API的抽象,简化了与消息代理的交互,支持消息生产者和消费者,以及消息监听器的实现。 10. **spring-expression-4.3.18.RELEASE.jar**:Spring表达式...

    Spring v3.0.0.RELEASE源码

    以上就是Spring v3.0.0.RELEASE的一些核心知识点,这些特性使得Spring在企业级应用开发中变得更加高效和灵活。通过对源码的学习,开发者可以深入了解Spring的工作原理,从而更好地利用它来构建高质量的Java应用程序...

    Spring Framework 3.2中文官方文档

    9. **消息支持**:Spring框架还支持消息传递,如JMS(Java消息服务),有助于实现异步通信和解耦应用组件。 10. **国际化(Internationalization,i18n)**:Spring提供了一套完整的i18n支持,包括消息源和资源包,...

    spring-framework-3.1.1.jar

    6. **测试支持**:Spring提供了强大的测试工具,包括Mock对象、测试上下文框架以及对JUnit和TestNG的集成,使得单元测试和集成测试变得简单。 7. **JSR-330标准支持**:Spring 3.1开始,对JSR-330(Dependency ...

    spring-framework-5.2.0+spring-security-5.3.1.pdf

    Spring Framework 5.2.0版提供了许多新特性,例如支持响应式编程模型Spring WebFlux,以处理异步非阻塞流和使用Reactive Streams的API。此外,Spring Framework 5.2.0还支持Kotlin编程语言,并加强了对Java 8及以上...

    spring3.0的demon

    8. **消息支持**:Spring 3.0提供了消息抽象,支持JMS(Java Message Service)和AMQP(Advanced Message Queuing Protocol),方便进行异步通信和解耦。 9. **国际化支持(i18n)**:Spring 3.0提供了处理多语言的...

    spring框架jar包-4.3.4版本

    - **Testing**:Spring提供了测试支持,包括单元测试和集成测试框架,使得编写和运行测试变得简单,可以对Spring应用的各个组件进行测试。 8. **模块**: - **Spring Expression Language (SpEL)**:这是一个强大...

    Flex + BlazeDS + Java + JMS 通信实例(附源码)

    JMS(Java Message Service)是Java平台中用于企业级消息传递的标准API,它提供了异步通信的能力,使得不同系统间可以可靠地交换消息。 本实例重点探讨了如何使用Flex作为前端,通过BlazeDS与Java后台结合JMS服务...

    Spring.docx

    - **数据访问/集成(Data Access/Integration)**:包括 JDBC、ORM、OXM 和 JMS 模块,用于数据库访问和集成,如 JPA、Hibernate、iBatis 等 ORM 框架的支持,以及 JMS 用于异步通信。 - **事务管理(Transaction...

    spring3.1帮助文档

    9. **测试支持**:Spring提供了一个测试模块,包含单元测试和集成测试工具,使得编写和执行测试变得简单。 在Spring 3.1版本中,引入了以下重要改进: - 支持Java 7特性,如类型推断(Type Inference)。 - 引入了...

    spring-framework-官方参考文档

    - **JMS(Java Message Service)**:用于处理异步消息传递。 6. **Web** - **Spring MVC**:强大的MVC框架,用于构建Web应用程序,提供了灵活的控制器、视图解析、数据绑定等功能。 - **Spring WebFlux**:响应...

Global site tag (gtag.js) - Google Analytics