pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
config类
config类是来完成声明交换机、声明队列、绑定交换机和队列这三个步骤的。代码如下:
@Configuration
public class RabbitmqFanoutConfig {
private String EXCHANGE_NAME = "fanoutExchange";
private String FANOUT_SMS_QUEUE = "fanout_sms_queue";
private String FANOUT_EMAIL_QUEUE = "fanout_email_queue";
//声明交换机
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange(EXCHANGE_NAME);
}
//声明队列
@Bean
public Queue fanoutEmailQueue(){
return new Queue(FANOUT_EMAIL_QUEUE);
}
@Bean
public Queue fanoutSmsQueue(){
return new Queue(FANOUT_SMS_QUEUE);
}
//绑定交换机和队列
@Bean
Binding bindingExchangeEmail(Queue fanoutEmailQueue, FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutEmailQueue).to(fanoutExchange);
}
@Bean
Binding bindingExchangeSms(Queue fanoutSmsQueue, FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutSmsQueue).to(fanoutExchange);
}
}
配置类的思路是,将整个配置类用@Configuration标注,作为bean注入到IOC容器中,而其中的@Bean标签下的每一个方法都会作为单独的bean注入到IOC容器当中。
首先声明交换机,根据前几节的声明可知,声明交换机同时需要有交换机名称和交换模式。 ```java channel.exchangeDeclare(EXCHANGE_ROUTING_INFO, BuiltinExchangeType.DIRECT); ``` 在本例中,由于使用@Bean注入,交换机名称默认为方法名,若需要自定义则可以用@Bean(" ")在其中指定。而交换机模式指定则是返回的方法,若是topics模式则返回TopicExchange;若是Direct模式则返回DirectExchange。如下所示:
@Bean
public TopicExchange topicExchange(){
return new TopicExchange(EXCHANGE_NAME);
}
@Bean
public DirectExchange directExchange(){
return new DirectExchange(EXCHANGE_NAME);
}
其次是声明队列,在前几节声明队列时,要指定队列名称、持久化、是否独占连接,、是否自动删除、一些扩展参数。如:
channel.queueDeclare(QUEUENAME,true,false,false,null);
而在本例中,是通过Queue的构造方法来实现队列的配置。
@Bean
public Queue fanoutEmailQueue(){
return new Queue(FANOUT_EMAIL_QUEUE);
}
最后是绑定交换机和队列,这一步需要有三个参数,队列名称、交换机名称和routingKey。代码如下:
@Bean
Binding bindingExchangeEmail(Queue fanoutEmailQueue, FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutEmailQueue).to(fanoutExchange);
}
方法里的参数对应的实际是IOC容器里的声明队列的ID和交换机ID。通过BindingBuilder中bind方法构建一个BindingBuilder的内部类——DestinationConfigurer类。(注意bind方法是静态方法)。BindingBuilder代码如下(节选):
public final class BindingBuilder {
private BindingBuilder() {
}
public static DestinationConfigurer bind(Queue queue) {
return new DestinationConfigurer(queue.getName(), DestinationType.QUEUE);
}
public static DestinationConfigurer bind(Exchange exchange) {
return new DestinationConfigurer(exchange.getName(), DestinationType.EXCHANGE);
}
之后调用DestinationConfigurer类中的to方法完成交换机与队列的绑定。代码如下:
public static final class DestinationConfigurer {
protected final String name; // NOSONAR
protected final DestinationType type; // NOSONAR
DestinationConfigurer(String name, DestinationType type) {
this.name = name;
this.type = type;
}
public Binding to(FanoutExchange exchange) {
return new Binding(this.name, this.type, exchange.getName(), "", new HashMap<String, Object>());
}
public HeadersExchangeMapConfigurer to(HeadersExchange exchange) {
return new HeadersExchangeMapConfigurer(this, exchange);
}
public DirectExchangeRoutingKeyConfigurer to(DirectExchange exchange) {
return new DirectExchangeRoutingKeyConfigurer(this, exchange);
}
public TopicExchangeRoutingKeyConfigurer to(TopicExchange exchange) {
return new TopicExchangeRoutingKeyConfigurer(this, exchange);
}
public GenericExchangeRoutingKeyConfigurer to(Exchange exchange) {
return new GenericExchangeRoutingKeyConfigurer(this, exchange);
}
}
上述代码可知重构了许多to方法,实际上,其他交换机模式都需要指定路由key,而这个路由key的指定。当to方法传入的是DirectExchange参数时,返回的DirectExchangeRoutingKeyConfigurer类如下:
public static final class DirectExchangeRoutingKeyConfigurer extends AbstractRoutingKeyConfigurer {
DirectExchangeRoutingKeyConfigurer(DestinationConfigurer destination, DirectExchange exchange) {
super(destination, exchange.getName());
}
public Binding with(String routingKey) {
return new Binding(destination.name, destination.type, exchange, routingKey,
Collections.<String, Object>emptyMap());
}
可见其中有一个with方法,这便是指定routingKey的方法。绑定完成。至此,config的配置就结束了。
生产者实现
生产者代码就十分简单,代码如下:
@Component
public class HelloProvider {
@Autowired
private RabbitTemplate rabbitTemplate;
private String EXCHANGE_NAME = "fanoutExchange";
public void send(){
rabbitTemplate.convertAndSend(EXCHANGE_NAME,"","你好世界!");
System.out.println("消息已经发送!");
}
}
消息发送使用springboot中的rabbitmq的模板类RabbitTemplate中的convertAndSend方法发送消息。代码如下:
@Override
public void convertAndSend(String exchange, String routingKey, final Object object) throws AmqpException {
convertAndSend(exchange, routingKey, object, (CorrelationData) null);
}
这里发送可指定routingKey。由于本例是订阅模式,因此routingKey为空。
消费者实现
消费者实现同样也很简单:
@Component
@RabbitListener(queuesToDeclare = @Queue("fanout_sms_queue")) //代表监听,消费者。
public class SmsConsumer {
@RabbitHandler
public void consume(String message){
System.out.println("sms消费者消息为:" + message);
}
}
通过注解@RabbitListener表明本类是消费者,启动后开启监听,监听队列在注解中用参数指定。类中方法可用@RabbitHandler注解表明是回调方法,接收到消息后执行该方法。
注意:如果生产者消费者是分布式的项目,要在生产者消费者代码中都添加config类,这样的话不管谁先启动,都能够顺利的将配置加载到rabbitMQ服务器上。