# 17.3.2　配置 Spring 支持 AMQP 消息

当我们第一次使用 Spring JMS 抽象的时候，首先配置了一个连接工厂。与之类似，使用 Spring AMQP 前也要配置一个连接工厂。只不过，所要配置的不是 JMS 的连接工厂，而是需要配置 AMQP 的连接工厂。更具体来讲，需要配置 RabbitMQ 连接工厂。

**什么是 RabbitMQ**

RabbitMQ 是一个流行的开源消息代理，它实现了 AMQP。Spring AMQP 为 RabbitMQ 提供了支持，包括 RabbitMQ 连接工厂、模板以及 Spring 配置命名空间。

在使用它发送和接收消息之前，你需要预先安装 RabbitMQ。我们可以在 [www.rabbitmq.com/download.html](http://www.rabbitmq.com/download.html) 上找到安装指南。根据你所运行的 OS 不同，这会有所差别，所以根据环境的不同，遵循相应指南进行安装的任务就留给读者自己完成。

配置 RabbitMQ 连接工厂最简单的方式就是使用 Spring AMQP 所提供的 rabbit 配置命名空间。为了使用这项功能，需要确保在 Spring 配置文件中已经声明了该模式：

```markup
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns="http://www.springframework.org/schema/rabbit"
  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
    http://www.springframework.org/schema/rabbit
    http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd" >
  
  ...
</beans>
```

尽管不是必须的，但我还选择在这个配置中将 rabbit 作为首选的命名空间，将 beans 作为第二位的命名空间。这是因为在这个配置中，我会更多的声明 rabbit 而不是 bean，这样的话，只会有少量的 bean 元素使用 “beans:” 前缀，而 rabbit 元素就能够避免使用前缀了。

rabbit 命名空间包含了多个在 Spring 中配置 RabbitMQ 的元素。但此时，你最感兴趣的可能就是。按照其最简单的形式，我们可以在配置 RabbitMQ 连接工厂的时候没有任何属性：

```markup
<connect-factory />
```

这的确能够运行起来，但是所导致的结果就是连接工厂 bean 没有可用的 bean ID，这样的话就难将连接工厂装配到需要它的 bean 中。因此，我们可能希望通过 id 属性为其设置一个 bean ID：

```markup
<connection-factory id="connectionFactory" />
```

默认情况下，连接工厂会假设 RabbitMQ 服务器监听 localhost 的 5672 端口，并且用户名和密码均为 guest。对于开发来讲，这是合理的默认值，但是对于生产环境，我们可能希望修改这些默认值。如下 \<connection-factory> 的设置重写了默认的做法：

```markup
<connection-factory id="connectionFactory"
                    host="${rabbitmq.host}"
                    username="${rabbitmq.username}"
                    password="${rabbitmq.password}" />
```

我们使用占位符来指定值，这样配置项可以在 Spring 配置文件之外进行管理（很可能位于属性文件中）。除了连接工厂以外，我们还要考虑使用其他的几个配置元素。接下来，看一下如何创建队列、Exchange 以及 binding。

**声明队列、Exchange 以及 binding**

在 JMS 中，队列和主题的路由行为都是通过规范建立的，AMQP 与之不同，它的路由更加丰富和灵活，依赖于如何定义队列和 Exchange 以及如何将它们绑定在一起。声明队列、Exchange 和 binding 的一种方式是使用 RabbitMQ Channel 接口的各种方法。但是直接使用 RabbitMQ 的 Channel 接口非常麻烦。Spring AMQP 能否帮助我们声明消息路由组件呢？

幸好，rabbit 命名空间包含了多个元素，帮助我们声明队列、Exchange 以及将它们结合在一起的 binding。表 17.3 中列出了这些元素。

| 元素                                | 作用                                           |
| --------------------------------- | -------------------------------------------- |
| \<queue>                          | 创建一个队列                                       |
| \<fanout-exchange>                | 创建一个 fanout 类型的 Exchange                     |
| \<header-exchange>                | 创建一个 header 类型的 Exchange                     |
| \<topic-exchange>                 | 创建一个 topic 类型的 Exchange                      |
| \<direct-exchange>                | 创建一个 direct 类型的 Exchange                     |
| \<bindings>\<binding>\</bindings> | 元素定义一个或多个元素的集合。元素创建  Exchange 和队列之间的 binding |

这些配置元素要与 \<admin> 元素一起使用。\<admin> 元素会创建一个 RabbitMQ 管理组件（administrative component），它会自动创建 （如果它们在 RabbitMQ 代理中尚未存在的话）上述这些元素所声明的队列、Exchange 以及 binding。

例如，如果你希望声明名为 spittle.alert.queue 的队列，只需要在 Spring 配置中添加如下的两个元素即可：

```markup
<admin connection-factory="connnectionFactory" />
<queue id="spittleAlertQueue" name="spittle.alerts" />
```

对于简单的消息来说，我们只需做这些就足够了。这是因为默认会有一个没有名称的 direct Exchange，所有的队列都会绑定到这个 Exchange 上，并且 routing key 与队列的名称相同。在这个简单的配置中，我们可以将消息发送到这个没有名称的 Exchange 上，并将 routing key 设定为 spittle.alert.queue，这样消息就会路由到这个队列中。实际上，我们重新创建了 JMS 的点对点模型。

但是，更加有意思的路由需要我们声明一个或更多的 Exchange，并将其绑定到队列上。例如，如果要将消息路由到多个队列中，而不管 routing key 是什么，我们可以按照如下的方式配置一个 fanout 以及多个队列：

```markup
<amdin connection-factory="connectionFactory" />
<queue name="spittle.alert.queue.1" >
<queue name="spittle.alert.queue.2" >
<queue name="spittle.alert.queue.3" >
<fanout-exchange name="spittle.fanout" >
  <bindings>
    <binding queue="spittle.alert.queue.1" />
    <binding queue="spittle.alert.queue.2" />
    <binding queue="spittle.alert.queue.3" />
  <bindings>
</fanout-exchange>
```

借助表 17.3 中的元素，会有无数种在 RabbitMQ 配置路由的方式，但是我却没有无尽的篇幅来为读者描述它们，所以为了让我们的讨论不至于偏离方向，我将这些创造性的路由作为练习留给读者，我将会继续讨论如何发送消息。


---

# 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://potoyang.gitbook.io/spring-in-action-v4/untitled-10/untitled-1/untitled-2.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.
