# Spring集成Artemis实现JSM的异步消息传递

***

JMS是一套Java定义的标准，用于在程序之间进行消息的流通。

## 一、概念逻辑

JMS的逻辑如下：

发布信息的称为JMS生产者，生产者将信息发送给JMS服务器，同时说明发送到的目的地（Destination），JMS服务器将消息保存在对应目的地的一个队列（queue）中，等待JMS消费者领取。

JMS消费者有两种领取信息的方式，一种是拉（pull）模式，即发出收取消息的请求，等待直到队列中有消息到达为止；一种是推（push）模式，即由容器监听目标队列，消息到达时通知对应的消费者进行处理。

这样的模式意味着信息的传送可以不一定是一对一的。对发送到队列的信息，消费者提取后即pop掉。

JMS服务器（MQ，消息队列，在Artemis里称broker）常用apeche的ActiveMQ，以及其新版本Artemis。在本地或远程部署Artemis实例后即可使用。

Spring提供了一套JMS实现，即JMSTemplate，可用它写出消费者和生产者，与Artemis交互。

## 二、引入依赖及配置

要在Spring项目中使用artemis，可在maven用springboot starter引入相应的框架：

```xml
<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-artemis</artifactId>
</dependency>
```

在application.yml中，可以对Artemis做一些配置。如果只是使用本地的Artemis broker实例，可以不做任何配置。

配置示例如下：

```yaml
spring:
  artemis:
    broker-url: tcp://api.mcyou.cc
    user: admin
    password: passwd
```

这里配置了broker的地址、用户名及密码。注意它是基于tcp的。

接下来要下载并创建artemis实例。首先在Apeche网站下载artemis，然后进入其lib，使用命令`artemis create 目标目录` 来在目标位置创建一个实例。创建时会要求输入想要的用户名及密码。

完成后来到实例的目录，进入/bin/，执行`artemis run`即可运行该实例。

## 三、创建生产者

```java
import org.apache.activemq.artemis.jms.client.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;

import javax.jms.Destination;

@Service
public class JmsMessagingService {
    private JmsTemplate jms;
    private Destination messageQueue = new ActiveMQQueue("cc.mcyou.queue");;

    @Autowired
    public JmsMessagingService(JmsTemplate jms){
        this.jms = jms;
    }

    public void sendUser(User user){
        jms.send(messageQueue, session -> session.createObjectMessage(user));
    }

    public void sendUser2(User user){
        jms.convertAndSend("cc.mcyou.queue", user);
    }
}
```

导入一个JmsTemplate，然后使用jms实例即可完成发送操作。

这里sendUser方法和sendUser2方法展示了两种发送对象的方式。sendUser方法使用jms.send方法，需要使用MessageCreator来构造Message；sendUser2方法直接使用convertAndSend方法，发送对象更方便一些。

对于每个发送，都要指定一个`Destination`。这里既可以构造一个Destination对象，也可以直接用字符串表明destination的名字，传递给artemis处理。

## 四、创建拉模式的消费者

```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;

@Component
public class JmsMessageReceiver {

    private JmsTemplate jms;

    @Autowired
    public JmsMessageReceiver(JmsTemplate jms){
        this.jms = jms;
    }

    public User receiveUser(){
        return (User)jms.receiveAndConvert("cc.mcyou.queue");
    }
}
```

还是通过Spring容器注入一个JmsTemplate的实例。在接收信息时，对应发送信息时的方法名，这里同样可以用`receive`方法或`receiveAndConvert`方法，且同样要指定一个接收的目的地。接收对象时直接用`receiveAndConvert`比较简单。如果用`receive`方法，需要手动注入一个`MessageConverter`对其进行转换。

注意对于拉模式的这种`receive`等方法，在调用时进程会被阻塞，等待信息到达。

## 五、创建推模式的消费者

```java
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Component
public class UserListener {

    @JmsListener(destination = "cc.mcyou.queue")
    public void receiveUser(User user){
        System.out.println("收到用户："+user.name);
    }
}
```

推模式是一个Listener的模式。使用注解`@JmsListener`注册监听器，同时指定destination。这样这个方法就交给Spring挂起，等待消息到达时由Spring调用这个方法，进行后续的处理。

推模式的最大好处在于不会阻塞进程，比较适合需要保证可用性的场景。

使用Spring提供的模板实现JMS，然后与Artemis通信，这样就使得限定于Java的JMS得以利用跨语言的Artemis进行通信。当然基于JMS的信息仍然必须由基于JMS的消费者接受才能利用。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://blog.mcyou.cc/hou-duan-xiang-guan/spring-ji-cheng-artemis-shi-xian-jsm-de-yi-bu-xiao-xi-chuan-di.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
