Spring整合RabbitMQ

Spring AMQP是对AMQP协议的抽象和封装,从官方网站上得知它是由两个项目组成的(spring-amqp和spring-rabbit)。在使用Spring整合RabbitMQ时我们主要关注三个核心接口(MessageListenerContainer、RabbitAdmin以及RabbitTemplate)

RabbitAdmin: 用于声明交换机 队列 绑定等
RabbitTemplate: 用于RabbitMQ消息的发送和接收
MessageListenerContainer: 监听容器 为消息入队提供异步处理

使用Spring整合RabbitMQ需要导入如下依赖

<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit</artifactId>
    <version>2.0.2.RELEASE</version>
</dependency>

本系列博客源码GIT地址:https://github.com/RobertoHuang/RGP-RABBITMQ.git

Spring AMQP手动声明

1.创建生产者配置类 将RabbitAdmin、RabbitTemplate纳入Spring管理

import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringAMQPProducerConfig {

	@Bean
	public ConnectionFactory connectionFactory() {
		com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();
		
		// 配置连接信息
		connectionFactory.setHost("192.168.0.4");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        
        // 网络异常自动连接恢复
        connectionFactory.setAutomaticRecoveryEnabled(true);
        // 每10秒尝试重试连接一次
        connectionFactory.setNetworkRecoveryInterval(10000);
        
        // 设置 ConnectionFactory 属性
        Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("principal", "RobertoHuang");
        properties.put("description", "RGP订单系统V2.0");
        properties.put("emailAddress", "RobertoHuang@foxmail.com");
        connectionFactory.setClientProperties(properties);
        
        return new CachingConnectionFactory(connectionFactory);
	}
	
	@Bean
	public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
		return new RabbitAdmin(connectionFactory);
	}
	
	@Bean
	public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
		return new RabbitTemplate(connectionFactory);
	}
	
}

2.创建生产者启动类

import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "com.johnfnash.learn.spring.rabbitmq.manual.producer")
public class ProducerApplication {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProducerApplication.class);
		
		RabbitAdmin rabbitAdmin = context.getBean(RabbitAdmin.class);
		RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
		
		// 声明交换机
		rabbitAdmin.declareExchange(new DirectExchange("roberto.order", true, false,new HashMap<String, Object>()));
	
		// 声明消息(消息体,消息属性)
		MessageProperties messageProperties = new MessageProperties();
		messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
		messageProperties.setContentType("UTF-8");
		Message message = new Message("订单消息".getBytes(), messageProperties);
		
		// 发布消息
		// 发布消息还可以使用rabbitTemplate.convertAndSend(); 其支持消息后置处理
		rabbitTemplate.send("roberto.order", "add", message);
		
//		context.close();
	}

}

3.创建消费者配置类 将RabbitAdmin纳入Spring管理,并在MessageListenerContainer类中定义了消息消费的逻辑

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.listener.MessageListenerContainer;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.ConsumerTagStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringAMQPConsumerConfig {

	@Bean
	public ConnectionFactory connectionFactory() {
		com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();
		
		// 配置连接信息
		connectionFactory.setHost("192.168.0.4");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        
        // 网络异常自动连接恢复
        connectionFactory.setAutomaticRecoveryEnabled(true);
        // 每10秒尝试重试连接一次
        connectionFactory.setNetworkRecoveryInterval(10000);
        
        // 设置ConnectionFactory属性信息
        Map<String, Object> connectionFactoryPropertiesMap = new HashMap<String, Object>();
        connectionFactoryPropertiesMap.put("principal", "RobertoHuang");
        connectionFactoryPropertiesMap.put("description", "RGP订单系统V2.0");
        connectionFactoryPropertiesMap.put("emailAddress", "RobertoHuang@foxmail.com");
        connectionFactory.setClientProperties(connectionFactoryPropertiesMap);
        
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(connectionFactory);
        return cachingConnectionFactory;
	}

	@Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }
	
	@Bean
	public MessageListenerContainer messageListener(ConnectionFactory connectionFactory) {
		SimpleMessageListenerContainer messageListenerContainer = new SimpleMessageListenerContainer();
		messageListenerContainer.setConnectionFactory(connectionFactory);
		messageListenerContainer.setQueueNames("roberto.order.add");
		
		// 设置消费者线程数
		messageListenerContainer.setConcurrentConsumers(5);
		// 设置最大消费者线程数
		messageListenerContainer.setMaxConcurrentConsumers(10);
		
		// 设置消费者属性信息
		Map<String, Object> argumentMap = new HashMap<String, Object>();
		messageListenerContainer.setConsumerArguments(argumentMap);
		
		// 设置消费者标签
		messageListenerContainer.setConsumerTagStrategy(new ConsumerTagStrategy() {
			@Override
			public String createConsumerTag(String queue) {
				return "RGP订单系统ADD处理逻辑消费者";
			}
		});
		
		// 使用setAutoStartup方法可以手动设置消息消费时机
		messageListenerContainer.setAutoStartup(false);
		
		// 使用setAfterReceivePostProcessors方法可以增加消息后置处理器
		//messageListenerContainer.setAfterReceivePostProcessors();
		
		messageListenerContainer.setMessageListener(new MessageListener() {
			@Override
			public void onMessage(Message message) {
				try {
					System.out.println(new String(message.getBody(), "UTF-8"));
					System.out.println(message.getMessageProperties());
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
		});

		return messageListenerContainer;
	}
	
}

4.创建消费者启动类

import java.util.HashMap;

import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.listener.MessageListenerContainer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "com.johnfnash.learn.spring.rabbitmq.manual.consumer")
public class ConsumerApplication {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerApplication.class);
		
		RabbitAdmin rabbitAdmin = context.getBean(RabbitAdmin.class);
		MessageListenerContainer messageListenerContainer = context.getBean(MessageListenerContainer.class);
		
		// 声明队列 (队列名", 是否持久化, 是否排他, 是否自动删除, 队列属性);
		rabbitAdmin.declareQueue(new Queue("roberto.order.add", true, false, false, new HashMap<>()));
		
		// 声明Direct Exchange (交换机名, 是否持久化, 是否自动删除, 交换机属性);
        rabbitAdmin.declareExchange(new DirectExchange("roberto.order", true, false, new HashMap<>()));

        // 将队列绑定到交换机上, Routing Key 为 add
        rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue("roberto.order.add")).to(new DirectExchange("roberto.order")).with("add"));
        
        // 开始监听队列
        messageListenerContainer.start();
       
//       context.close();
	}

}

5.依次启动消息消费者和生产者 控制台输出如下

订单信息
MessageProperties [headers={}, contentType=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=roberto.order, receivedRoutingKey=add, deliveryTag=1, consumerTag=RGP订单系统ADD处理逻辑消费者, consumerQueue=roberto.order.add]

上诉代码使用Spring整合RabbitMQ,最终实现效果和上一篇博客介绍RabbitMQ Java Client一致

Spring AMQP自动声明

在上诉的Demo中我们是手动使用RabbitAdmin对交换机、队列和绑定进行声明的,Spring AMQP还提供了自动声明方式交换机、队列和绑定。我们可以直接把要自动声明的组件纳入Spring容器中管理即可,自动声明发生在RabbitMQ第一次连接创建的时候,自动声明支持单个和批量自动声明。使用自动声明需要符合如下条件:

  • 需要有连接产生
  • RabbitAdmin必须交由Spring管理,且autoStartup必须为true(默认)
  • 如果ConnectionFactory使用的是CachingConnectionFactory,则cacheMode必须要为CacheMode.CHANNEL
  • 所有要声明的组件的shouldDeclare必须为true
  • 要声明的Queue名称不能以amq.开头

上诉规则定义在RabbitAdmin的afterPropertiesSet方法中,有兴趣的同学可以自行阅读RabbitAdmin源码

1.创建生产者配置类 将RabbitAdmin、RabbitTemplate纳入Spring管理

@Configuration
public class SpringAMQPProducerConfig {

	@Bean
	public ConnectionFactory connectionFactory() {
		com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();
		
		// 配置连接信息
		connectionFactory.setHost("192.168.0.4");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        
        // 网络异常自动连接恢复
        connectionFactory.setAutomaticRecoveryEnabled(true);
        // 每10秒尝试重试连接一次
        connectionFactory.setNetworkRecoveryInterval(10000);
        
        // 设置 ConnectionFactory 属性
        Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("principal", "RobertoHuang");
        properties.put("description", "RGP订单系统V2.0");
        properties.put("emailAddress", "RobertoHuang@foxmail.com");
        connectionFactory.setClientProperties(properties);
        
        return new CachingConnectionFactory(connectionFactory);
	}
	
	@Bean
	public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
		return new RabbitAdmin(connectionFactory);
	}
	
	@Bean
	public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
		return new RabbitTemplate(connectionFactory);
	}
	
}

2.创建生产者启动类

@ComponentScan(basePackages = "com.johnfnash.learn.spring.rabbitmq.auto.producer")
public class ProducerApplication {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProducerApplication.class);
		
		RabbitAdmin rabbitAdmin = context.getBean(RabbitAdmin.class);
		RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
		
		// 声明交换机
		rabbitAdmin.declareExchange(new DirectExchange("roberto.order", true, false,new HashMap<String, Object>()));
	
		// 声明消息(消息体,消息属性)
		MessageProperties messageProperties = new MessageProperties();
		messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
		messageProperties.setContentType("UTF-8");
		Message message = new Message("订单消息".getBytes(), messageProperties);
		
		// 发布消息
		// 发布消息还可以使用rabbitTemplate.convertAndSend(); 其支持消息后置处理
		rabbitTemplate.send("roberto.order", "add", message);
		
//		context.close();
	}

}

3.创建消费者配置类 将RabbitAdmin纳入Spring管理,并在MessageListenerContainer类中定义了消息消费的逻辑,并且在该配置类中声明交换机,队列,绑定

@Configuration
public class SpringAMQPConsumerConfig {

	@Bean
	public ConnectionFactory connectionFactory() {
		com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();
		
		// 配置连接信息
		connectionFactory.setHost("192.168.0.4");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        
        // 网络异常自动连接恢复
        connectionFactory.setAutomaticRecoveryEnabled(true);
        // 每10秒尝试重试连接一次
        connectionFactory.setNetworkRecoveryInterval(10000);
        
        // 设置ConnectionFactory属性信息
        Map<String, Object> connectionFactoryPropertiesMap = new HashMap<String, Object>();
        connectionFactoryPropertiesMap.put("principal", "RobertoHuang");
        connectionFactoryPropertiesMap.put("description", "RGP订单系统V2.0");
        connectionFactoryPropertiesMap.put("emailAddress", "RobertoHuang@foxmail.com");
        connectionFactory.setClientProperties(connectionFactoryPropertiesMap);
        
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(connectionFactory);
        return cachingConnectionFactory;
	}

	@Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }
	
	// 自动声明交换机
    // 如果要一次性声明多个 使用public List<Exchange> listExchange()即可
	@Bean
	public Exchange exchange() {
		return new DirectExchange("roberto.order", true, false, new HashMap<>());
	}
	
	@Bean
    // 自动声明队列
    // 如果要一次性声明多个 使用public List<Queue> listQueue()即可
    public Queue queue() {
		return new Queue("roberto.order.add", true, false, false, new HashMap<>());
	}
	
	@Bean
	// 自动声明绑定
    // 如果要一次性声明多个 使用public List<Binding> listBinding()即可
	public Binding Binding() {
		return new Binding("roberto.order.add", Binding.DestinationType.QUEUE
				, "roberto.order", "add", new HashMap<>());
	}
	
	@Bean
	public MessageListenerContainer messageListener(ConnectionFactory connectionFactory) {
		SimpleMessageListenerContainer messageListenerContainer = new SimpleMessageListenerContainer();
		messageListenerContainer.setConnectionFactory(connectionFactory);
		messageListenerContainer.setQueueNames("roberto.order.add");
		
		// 设置消费者线程数
		messageListenerContainer.setConcurrentConsumers(5);
		// 设置最大消费者线程数
		messageListenerContainer.setMaxConcurrentConsumers(10);
		
		// 设置消费者属性信息
		Map<String, Object> argumentMap = new HashMap<String, Object>();
		messageListenerContainer.setConsumerArguments(argumentMap);
		
		// 设置消费者标签
		messageListenerContainer.setConsumerTagStrategy(new ConsumerTagStrategy() {
			@Override
			public String createConsumerTag(String queue) {
				return "RGP订单系统ADD处理逻辑消费者";
			}
		});
		
		// 使用setAutoStartup方法可以手动设置消息消费时机
		// 注意:Spring AMQP自动声明的情况下必须设置为 true
		messageListenerContainer.setAutoStartup(true);
		
		// 使用setAfterReceivePostProcessors方法可以增加消息后置处理器
		//messageListenerContainer.setAfterReceivePostProcessors();
		
		messageListenerContainer.setMessageListener(new MessageListener() {
			@Override
			public void onMessage(Message message) {
				try {
					System.out.println(new String(message.getBody(), "UTF-8"));
					System.out.println(message.getMessageProperties());
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
		});

		return messageListenerContainer;
	}
	
}

4.创建消费者启动类

@ComponentScan(basePackages = "com.johnfnash.learn.spring.rabbitmq.auto.consumer")
public class ConsumerApplication {

	@SuppressWarnings({ "unused", "resource" })
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerApplication.class);
		
//       context.close();
	}

}

5.依次启动消息消费者和生产者 控制台输出如下

订单信息
MessageProperties [headers={}, contentType=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=roberto.order, receivedRoutingKey=add, deliveryTag=1, consumerTag=RGP订单系统ADD处理逻辑消费者, consumerQueue=roberto.order.add]

该代码与上诉代码实现的效果一致,只是将交换机,队列,绑定进行了自动声明

MessageListenerAdapte

以上两个Demo在消费消息处理逻辑时往MessageListenerContainer中传递了MessageListener,但是我们有时候已经写好了消费逻辑对应的类,我们不希望它去扩展MessageListener/ChannelAwareMessageListener,因为这么做的话意味着我们需要改变现有代码。Spring AMQP提供了消息处理器适配器的功能,它可以把一个纯POJO类适配成一个可以处理消息的处理器,默认处理消息的方法为handleMessage,可以通过setDefaultListenerMethod方法进行修改

1.创建生产者配置类

@Configuration
public class SpringAMQPProducerConfig {

	@Bean
	public ConnectionFactory connectionFactory() {
		com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();
		
		// 配置连接信息
		connectionFactory.setHost("192.168.0.4");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        
        // 网络异常自动连接恢复
        connectionFactory.setAutomaticRecoveryEnabled(true);
        // 每10秒尝试重试连接一次
        connectionFactory.setNetworkRecoveryInterval(10000);
        
        // 设置 ConnectionFactory 属性
        Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("principal", "RobertoHuang");
        properties.put("description", "RGP订单系统V2.0");
        properties.put("emailAddress", "RobertoHuang@foxmail.com");
        connectionFactory.setClientProperties(properties);
        
        return new CachingConnectionFactory(connectionFactory);
	}
	
	@Bean
	public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
		return new RabbitAdmin(connectionFactory);
	}
	
	@Bean
	public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
		return new RabbitTemplate(connectionFactory);
	}
	
}

2.创建生产者启动类

@ComponentScan(basePackages = "com.johnfnash.learn.spring.rabbitmq.adapter.producer")
public class ProducerApplication {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProducerApplication.class);
		
		RabbitAdmin rabbitAdmin = context.getBean(RabbitAdmin.class);
		RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
		
		// 声明交换机
		rabbitAdmin.declareExchange(new DirectExchange("roberto.order", true, false,new HashMap<String, Object>()));
	
		// 声明消息(消息体,消息属性)
		MessageProperties messageProperties = new MessageProperties();
		messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
		messageProperties.setContentType("UTF-8");
		Message message = new Message("订单消息".getBytes(), messageProperties);
		
		// 发布消息
		// 发布消息还可以使用rabbitTemplate.convertAndSend(); 其支持消息后置处理
		rabbitTemplate.send("roberto.order", "add", message);
		
//		context.close();
	}

}

3.创建消费者消息处理器类,它可是是纯POJO类

public class MessageHandle {

	public void add(byte[] message) {
		try {
			System.out.println(new String(message,"UTF-8"));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}
	
}

4.创建消费者配置类 配置自定义消息处理器(将roberto.order.add队列使用自定义消息处理类的add方法进行处理)

@Configuration
public class SpringAMQPConsumerConfig {

	@Bean
	public ConnectionFactory connectionFactory() {
		com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();
		
		// 配置连接信息
		connectionFactory.setHost("192.168.0.4");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        
        // 网络异常自动连接恢复
        connectionFactory.setAutomaticRecoveryEnabled(true);
        // 每10秒尝试重试连接一次
        connectionFactory.setNetworkRecoveryInterval(10000);
        
        // 设置ConnectionFactory属性信息
        Map<String, Object> connectionFactoryPropertiesMap = new HashMap<String, Object>();
        connectionFactoryPropertiesMap.put("principal", "RobertoHuang");
        connectionFactoryPropertiesMap.put("description", "RGP订单系统V2.0");
        connectionFactoryPropertiesMap.put("emailAddress", "RobertoHuang@foxmail.com");
        connectionFactory.setClientProperties(connectionFactoryPropertiesMap);
        
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(connectionFactory);
        return cachingConnectionFactory;
	}

	@Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }
	
	// 自动声明交换机
    // 如果要一次性声明多个 使用public List<Exchange> listExchange()即可
	@Bean
	public Exchange exchange() {
		return new DirectExchange("roberto.order", true, false, new HashMap<>());
	}
	
	@Bean
    // 自动声明队列
    // 如果要一次性声明多个 使用public List<Queue> listQueue()即可
    public Queue queue() {
		return new Queue("roberto.order.add", true, false, false, new HashMap<>());
	}
	
	@Bean
	// 自动声明绑定
    // 如果要一次性声明多个 使用public List<Binding> listBinding()即可
	public Binding Binding() {
		return new Binding("roberto.order.add", Binding.DestinationType.QUEUE
				, "roberto.order", "add", new HashMap<>());
	}
	
	@Bean
	public MessageListenerContainer messageListener(ConnectionFactory connectionFactory) {
		SimpleMessageListenerContainer messageListenerContainer = new SimpleMessageListenerContainer();
		messageListenerContainer.setConnectionFactory(connectionFactory);
		messageListenerContainer.setQueueNames("roberto.order.add");
		
		// 设置消费者线程数
		messageListenerContainer.setConcurrentConsumers(5);
		// 设置最大消费者线程数
		messageListenerContainer.setMaxConcurrentConsumers(10);
		
		// 设置消费者属性信息
		Map<String, Object> argumentMap = new HashMap<String, Object>();
		messageListenerContainer.setConsumerArguments(argumentMap);
		
		// 设置消费者标签
		messageListenerContainer.setConsumerTagStrategy(new ConsumerTagStrategy() {
			@Override
			public String createConsumerTag(String queue) {
				return "RGP订单系统ADD处理逻辑消费者";
			}
		});
		
		// 使用setAutoStartup方法可以手动设置消息消费时机
		// 注意:Spring AMQP自动声明的情况下必须设置为 true
		messageListenerContainer.setAutoStartup(true);
		
		// 使用setAfterReceivePostProcessors方法可以增加消息后置处理器
		//messageListenerContainer.setAfterReceivePostProcessors();
		
		// 新建消息处理器适配器
		MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageHandle());
		// 设置默认处理消息方法
		adapter.setDefaultListenerMethod("handleMessage");
		// 将roberto.order.add队列的消息 使用add方法进行处理
		Map<String, String> queueOrTagToMethodName = new HashMap<>();
		queueOrTagToMethodName.put("roberto.order.add", "add");
		adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
		messageListenerContainer.setMessageListener(adapter);
		
		return messageListenerContainer;
	}
	
}

5.创建消费者启动类

public class ConsumerApplication {

	@SuppressWarnings({ "unused", "resource" })
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerApplication.class);
		
//       context.close();
	}

}

6.依次启动消息消费者和生产者 控制台输出如下

订单信息

如上Demo说明我们可以将一个纯POJO类定义为消息处理器,并且不用去扩展MessageListener/ChannelAwareMessageListener接口,关于自定义处理器方法的参数默认情况下为byte[]类型,这是由Spring AMQP默认消息转换器(SimpleMessageConverter)决定的,接下来我们将介绍Spring AMQP的消息转换器功能

Spring AMQP消息转换器

在上诉例子中我们定义的add(byte[] message)方法的参数是一个字节数组,但是有时候我们往RabbitMQ中发送的是一个JSON对象,我们希望在处理消息的时候它已经自动帮我们转为JAVA对象;又或者我们往RabbitMQ中发送的是一张图片或其他格式的文件,我们希望在处理消息的时候它已经自动帮我们转成文件格式,我们可以手动设置MessageConverter来实现如上需求,如果未设置MessageConverter则使用Spring AMQP默认提供的SimpleMessageConverter

以下例子使用MessageConverter实现了当生产者往RabbitMQ发送不同类型的数据的时候,使用MessageHandle不同的方法进行处理,需要注意的是当生产者在发送JSON数据的时候,需要制定这个JSON是哪个对象,用于Spring AMQP转换,规则如下

当发送普通对象的JSON数据时,需要在消息的header中增加一个__TypeId__的属性告知消费者是哪个对象

当发送List集合对象的JSON数据时,需要在消息的header中将__TypeId__指定为java.util.List,并且需要额外指定属性__ContentTypeId__用户告知消费者List集合中的对象类型

当发送Map集合对象的JSON数据时,需要在消息的header中将__TypeId__指定为java.util.Map,并且需要额外指定属性__KeyTypeId__用于告知客户端Map中key的类型,__ContentTypeId__用于告知客户端Map中Value的类型

1.创建订单实体类

public class Order {

	 /**
     * 订单编号
     **/
    private String orderId;

    /**
     * 订单金额
     **/
    private BigDecimal orderAmount;

    public Order() {

    }

    public Order(String orderId, BigDecimal orderAmount) {
        this.orderId = orderId;
        this.orderAmount = orderAmount;
    }

	// setter, getter

}

2.自定义文件消息转换器

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.util.FileCopyUtils;

// 自定义文件消息转换器
public class FileMessageConverter implements MessageConverter {

	@Override
	public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
		return null;
	}

	@Override
	public Object fromMessage(Message message) throws MessageConversionException {
		String extName = (String) message.getMessageProperties().getHeaders().get("_extName");
		byte[] bytes = message.getBody();
        String fileName = UUID.randomUUID().toString();
        String filePath = System.getProperty("java.io.tmpdir") + fileName + "." + extName;
        File tempFile = new File(filePath);
        try {
			FileCopyUtils.copy(bytes, tempFile);
		} catch (IOException e) {
			e.printStackTrace();
		}
        
		return tempFile;
	}

}

3.自定义字符串消息转换器

// 自定义字符串消息转换器
public class StringMessageConverter implements MessageConverter {

	@Override
	public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
		return null;
	}

	@Override
	public Object fromMessage(Message message) throws MessageConversionException {
		try {
            return new String(message.getBody(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new MessageConversionException("StringMessageConverter转换失败", e);
        }
	}

}

4.自定义消息处理器类

// 自定义消息处理器类
public class MessageHandle {

	public void add(byte[] body) {
		System.out.println("----------byte[]方法进行处理----------");
		System.out.println("body");
	}

	public void add(String message) {
		System.out.println("----------String方法进行处理----------");
		System.out.println(message);
	}

	public void add(File file) {
		System.out.println("----------File方法进行处理----------");
		System.out.println(file.length());
		System.out.println(file.getName());
		System.out.println(file.getAbsolutePath());
	}

	public void add(Order order) {
		System.out.println("----------Order方法进行处理----------");
		System.out.println(order.getOrderId() + "---" + order.getOrderAmount());
	}

	public void add(List<Order> orderList) {
		System.out.println("----------List<Order>方法进行处理----------");
		System.out.println(orderList.size());
		for (Order order : orderList) {
			System.out.println(order.getOrderId() + "---" + order.getOrderAmount());
		}
	}

	public void add(Map<String, Order> orderMap) {
		System.out.println("----------Map<String, Order>方法进行处理----------");
		for (Map.Entry<String, Order> entry : orderMap.entrySet()) {
			System.out.println(entry.getKey());
			System.out.println(entry.getValue().getOrderId() + "---" + entry.getValue().getOrderAmount());
		}
	}

}

5.创建生产者配置类

@Configuration
public class SpringAMQPProducerConfig {

	@Bean
	public ConnectionFactory connectionFactory() {
		com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();
		
		// 配置连接信息
		connectionFactory.setHost("192.168.0.4");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        
        // 网络异常自动连接恢复
        connectionFactory.setAutomaticRecoveryEnabled(true);
        // 每10秒尝试重试连接一次
        connectionFactory.setNetworkRecoveryInterval(10000);
        
        // 设置 ConnectionFactory 属性
        Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("principal", "RobertoHuang");
        properties.put("description", "RGP订单系统V2.0");
        properties.put("emailAddress", "RobertoHuang@foxmail.com");
        connectionFactory.setClientProperties(properties);
        
        return new CachingConnectionFactory(connectionFactory);
	}
	
	@Bean
	public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
		return new RabbitAdmin(connectionFactory);
	}
	
	@Bean
	public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
		return new RabbitTemplate(connectionFactory);
	}
	
}

6.创建生产者启动类 发送多条不同消息类型消息

@ComponentScan(basePackages = "com.johnfnash.learn.spring.rabbitmq.message.converter.producer")
public class ProducerApplication {

	private static final String APPLICATION_JSON = "application/json";
	private static final String CONTENT_TYPE_ID = "__ContentTypeId__";
	private static final String TYPE_ID = "__TypeId__";
	private static final String ROUTING_KEY = "add";
	private static final String EXCHANGE_NAME = "roberto.order";

	@SuppressWarnings("resource")
	public static void main(String[] args) throws Exception {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProducerApplication.class);

		RabbitAdmin rabbitAdmin = context.getBean(RabbitAdmin.class);
		RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);

		// 声明交换机
		rabbitAdmin.declareExchange(new DirectExchange(EXCHANGE_NAME, true, false, new HashMap<String, Object>()));

		// 发布消息
		sendString(rabbitTemplate);
		sendSingle(rabbitTemplate);
		sendList(rabbitTemplate);
		sendMap(rabbitTemplate);
		
		// context.close();
	}

	public static void sendString(RabbitTemplate rabbitTemplate) {
		// 声明消息 (消息体, 消息属性)
		MessageProperties messageProperties = new MessageProperties();
		messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
		messageProperties.setContentType("text/plain");
		Message message = new Message("订单消息".getBytes(), messageProperties);

		rabbitTemplate.send(EXCHANGE_NAME, ROUTING_KEY, message);
	}

	public static void sendSingle(RabbitTemplate rabbitTemplate) throws Exception {
		Order order = new Order("OD0000001", new BigDecimal(888888.888888));
		ObjectMapper mapper = new ObjectMapper();

		// 声明消息 (消息体, 消息属性)
		MessageProperties properties = new MessageProperties();
		properties.getHeaders().put(TYPE_ID, "order");
		properties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
		properties.setContentType(APPLICATION_JSON);
		Message message = new Message(mapper.writeValueAsString(order).getBytes(), properties);

		rabbitTemplate.send(EXCHANGE_NAME, ROUTING_KEY, message);
	}

	public static void sendList(RabbitTemplate rabbitTemplate) throws Exception {
		Order order = new Order("OD0000001", new BigDecimal(888888.888888));
		Order order2 = new Order("OD0000002", new BigDecimal(888888.888888));
		List<Order> orderList = Arrays.asList(order, order2);

		ObjectMapper mapper = new ObjectMapper();
		// 声明消息 (消息体, 消息属性)
		MessageProperties properties = new MessageProperties();
		properties.getHeaders().put(TYPE_ID, "java.util.List");
		properties.getHeaders().put(CONTENT_TYPE_ID, "order");
		properties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
		properties.setContentType(APPLICATION_JSON);
		Message message = new Message(mapper.writeValueAsString(orderList).getBytes(), properties);

		rabbitTemplate.send(EXCHANGE_NAME, ROUTING_KEY, message);
	}

	public static void sendMap(RabbitTemplate rabbitTemplate) throws Exception {
		Order order = new Order("OD0000001", new BigDecimal(888888.888888));
		Order order2 = new Order("OD0000002", new BigDecimal(888888.888888));
		Map<String, Order> orderMap = new HashMap<>();
		orderMap.put(order.getOrderId(), order);
		orderMap.put(order2.getOrderId(), order2);

		ObjectMapper objectMapper = new ObjectMapper();
		// 声明消息 (消息体, 消息属性)
		MessageProperties properties = new MessageProperties();
		properties.getHeaders().put(TYPE_ID, "java.util.Map");
		properties.getHeaders().put("__KeyTypeId__", "java.lang.String");
		properties.getHeaders().put(CONTENT_TYPE_ID, "order");
		properties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
		properties.setContentType(APPLICATION_JSON);
		Message message = new Message(objectMapper.writeValueAsString(orderMap).getBytes(), properties);

		rabbitTemplate.send(EXCHANGE_NAME, ROUTING_KEY, message);
	}

}

7.创建消费者配置类 为不同ContentType消息设置消息转换器

@Configuration
public class SpringAMQPConsumerConfig {

	@Bean
	public ConnectionFactory connectionFactory() {
		com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();
		
		// 配置连接信息
		connectionFactory.setHost("192.168.0.4");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        
        // 网络异常自动连接恢复
        connectionFactory.setAutomaticRecoveryEnabled(true);
        // 每10秒尝试重试连接一次
        connectionFactory.setNetworkRecoveryInterval(10000);
        
        // 设置ConnectionFactory属性信息
        Map<String, Object> connectionFactoryPropertiesMap = new HashMap<String, Object>();
        connectionFactoryPropertiesMap.put("principal", "RobertoHuang");
        connectionFactoryPropertiesMap.put("description", "RGP订单系统V2.0");
        connectionFactoryPropertiesMap.put("emailAddress", "RobertoHuang@foxmail.com");
        connectionFactory.setClientProperties(connectionFactoryPropertiesMap);
        
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(connectionFactory);
        return cachingConnectionFactory;
	}

	@Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }
	
	// 自动声明交换机
    // 如果要一次性声明多个 使用public List<Exchange> listExchange()即可
	@Bean
	public Exchange exchange() {
		return new DirectExchange("roberto.order", true, false, new HashMap<>());
	}
	
	@Bean
    // 自动声明队列
    // 如果要一次性声明多个 使用public List<Queue> listQueue()即可
    public Queue queue() {
		return new Queue("roberto.order.add", true, false, false, new HashMap<>());
	}
	
	@Bean
	// 自动声明绑定
    // 如果要一次性声明多个 使用public List<Binding> listBinding()即可
	public Binding Binding() {
		return new Binding("roberto.order.add", Binding.DestinationType.QUEUE
				, "roberto.order", "add", new HashMap<>());
	}
	
	@Bean
	public MessageListenerContainer messageListener(ConnectionFactory connectionFactory) {
		SimpleMessageListenerContainer messageListenerContainer = new SimpleMessageListenerContainer();
		messageListenerContainer.setConnectionFactory(connectionFactory);
		messageListenerContainer.setQueueNames("roberto.order.add");
		
		// 设置消费者线程数
		messageListenerContainer.setConcurrentConsumers(5);
		// 设置最大消费者线程数
		messageListenerContainer.setMaxConcurrentConsumers(10);
		
		// 设置消费者属性信息
		Map<String, Object> argumentMap = new HashMap<String, Object>();
		messageListenerContainer.setConsumerArguments(argumentMap);
		
		// 设置消费者标签
		messageListenerContainer.setConsumerTagStrategy(new ConsumerTagStrategy() {
			@Override
			public String createConsumerTag(String queue) {
				return "RGP订单系统ADD处理逻辑消费者";
			}
		});
		
		// 使用setAutoStartup方法可以手动设置消息消费时机
		// 注意:Spring AMQP自动声明的情况下必须设置为 true
		messageListenerContainer.setAutoStartup(true);
		
		// 使用setAfterReceivePostProcessors方法可以增加消息后置处理器
		//messageListenerContainer.setAfterReceivePostProcessors();
		
		// 新建消息处理器适配器
		MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageHandle());
		// 设置默认处理消息方法
		adapter.setDefaultListenerMethod("handleMessage");
		// 将roberto.order.add队列的消息 使用add方法进行处理
		Map<String, String> queueOrTagToMethodName = new HashMap<>();
		queueOrTagToMethodName.put("roberto.order.add", "add");
		adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
		messageListenerContainer.setMessageListener(adapter);
		
		// 设置消息转换器
		ContentTypeDelegatingMessageConverter converter = 
				new ContentTypeDelegatingMessageConverter();
		StringMessageConverter stringMessageConverter = new StringMessageConverter();
		FileMessageConverter fileMessageConverter = new FileMessageConverter();
		Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
		Map<String, Class<?>> idClassMapping = new HashMap<>();
        idClassMapping.put("order", Order.class);
        DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
        javaTypeMapper.setIdClassMapping(idClassMapping);
        jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
		
        // 设置text/html text/plain 使用StringMessageConverter
        converter.addDelegate("text/html", stringMessageConverter);
        converter.addDelegate("text/plain", stringMessageConverter);
        // 设置application/json 使用Jackson2JsonMessageConverter
        converter.addDelegate("application/json", jackson2JsonMessageConverter);
        // 设置image/jpg image/png 使用FileMessageConverter
        converter.addDelegate("image/jpg", fileMessageConverter);
        converter.addDelegate("image/png", fileMessageConverter);
        adapter.setMessageConverter(converter);
        
		return messageListenerContainer;
	}
	
}

8.创建消费者启动类

@ComponentScan(basePackages = "com.johnfnash.learn.spring.rabbitmq.message.converter.consumer")
public class ConsumerApplication {

	@SuppressWarnings({ "unused", "resource" })
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerApplication.class);
		
//       context.close();
	}

}

9.依次启动消息消费者和生产者 控制台输出如下

----------String方法进行处理----------
订单消息

----------File方法进行处理----------
18758
403e735f-88ff-4bbe-b7d3-a07766de7df9.jpg
C:\Users\ADMINI~1\AppData\Local\Temp\403e735f-88ff-4bbe-b7d3-a07766de7df9.jpg

----------Order方法进行处理----------
OD0000001---888888.888888

----------List<Order>方法进行处理----------
2
OD0000001---888888.888888
OD0000002---888888.888888

----------Map<String, Order>方法进行处理----------
OD0000001
OD0000001---888888.888888
OD0000002
OD0000002---888888.888888

在经过消息转化器后,Spring AMQP会根据最后转换结果的类型找到对应的消息处理方法,在本章中我们只介绍了消息转换器的fromMessage()方法,关于toMessage()将在后续博客中提及

本文转自:(三) RabbitMQ实战教程(面向Java开发人员)之Spring整合RabbitMQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值