一、Spring使用JmsTemplate简化对JMS的访问
在JAVA对JMS队列访问中,使用默认的JMS支持将存在大量的检查型异常。通过Spring的支持,可以将所有的JMS的检查型异常转换为运行时非检查异常。以及在Spring中,通过配置JMSConnectionFactory的DefaultDestinationName指定发送和接收目的地。
下面是ActiveMQ的连接factory配置:
1 @Bean
2 public ActiveMQConnectionFactory getAMQFactory() {
3 ActiveMQConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory();
4 mqConnectionFactory.setBrokerURL("tcp://59.110.231.87:61616");
5 mqConnectionFactory.setTrustedPackages(Arrays.asList("com.edoctor.bean"));
6 return mqConnectionFactory;
7 }
下面是JmsTemplate的配置:
1 @Bean
2 public JmsTemplate getJmsTemplate(ActiveMQConnectionFactory cf, MessageConverter messageConverter) {
3 JmsTemplate jmsTemplate = new JmsTemplate(cf);
4 jmsTemplate.setDefaultDestinationName("com.demo.testActiveMQ");
5 jmsTemplate.setMessageConverter(messageConverter);
6 // pubSubDomain = true 为队列模式,false为订阅发布模式
7 jmsTemplate.setPubSubDomain(false);
8 return jmsTemplate;
9 }
我是使用纯JAVA注解配置的Bean,基于xml的也类似,可以自行搜索。
上述字段含义如下:
setDefaultDestinationName:设置ActiveMQ的队列名称,当然如果下面的pubSubDomain为true,则为主题名称
setMessageConverter:设置ActiveMQ的消息转换器,默认不写的话是使用的Spring的SimpleMessageConverter
setPubSubDomain:值true代表该Template为队列,false为主题
二、Spring的消息转换器的种类
Spring自带的消息转换器可以大大简化消息的读取以及写入,所有的消息转换器都位于org.springframework.jms.support.converter包中。
消息转换器
功能
MappingJacksonMessageConverter
使用Jackson JSON库实现消息与JSON格式之间的相互转换
MappingJackson2MessageConverter
使用Jackson 2 JSON库实现消息与JSON格式之间相互转换
MarshallingMessageConverter
使用JAXB库实现消息与XML格式之间的相互转换
SimpleMessageConverter
实现String与TextMessage之间的相互转换,字节数组与Bytes
Message之间的相互转换,Map与MapMessage之间的相互转换
以及Serializable对象与ObjectMessage之间的相互转换
默认情况下,JmsTemplate在convertAndSend()方法中会使用SimpleMessage Converter。但是通过将消息转换器声明为bean并将其注入到JmsTemplate的messageConverter属性中,我们可以重写这种行为。例如,如果你想使用JSON消息的话,那么可以声明一个MappingJackson2MessageConverter bean。
三、配置MappingJackson2MessageConverter的Bean
上文的JmsTemplate已经成功注入了ActiveMQConnectionFactory,下面就将注入我们的MessageConverter。
由于使用默认的SimpleMessageConverter如果是Object对象的话,必须将对象序列化,如果对象包含包装类例如Integer将无法实现序列化,因此,我打算使用基于json的MappingJackson2MessageConverter序列化对象发送以及接受,该配置会实现自动序列化。但是在网上查阅相关文档,发现几乎没有中文介绍配置MappingJackson2MessageConverter的,因此,希望写下这个配置帮助大家。
MappingJackson2MessageConverter在JavaDoc中的详细配置:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jms/support/converter/MappingJackson2MessageConverter.html
对于文档和介绍,上述链接有详细说明,里面主要提到一点:
意思是,如果需要在接受Object对象格式的消息的时候,需要配置这项属性,以及这项属性需要参考typeIdMappings。
我们再看一下这个Map的说明:
这个Map以String为key,Class为值,而这里的Key就是对应的typeId,Value就是你需要序列化的类。所以只需要构建这样一个Map就可以允许从MQ中接受类对象型的消息了。下面是我的POJO(注意该在MQ中发送接收的对象务必有无参构造函数)
1 public class TestJMS {
2 private String name;
3 private Integer age;
4 private String email;
5
6 public TestJMS() {
7 }
8
9 public TestJMS(String name, Integer age, String email) {
10 this.name = name;
11 this.age = age;
12 this.email = email;
13 }
14
15 public String getName() {
16 return name;
17 }
18
19 public void setName(String name) {
20 this.name = name;
21 }
22
23 public Integer getAge() {
24 return age;
25 }
26
27 public void setAge(Integer age) {
28 this.age = age;
29 }
30
31 public String getEmail() {
32 return email;
33 }
34
35 public void setEmail(String email) {
36 this.email = email;
37 }
38
39 @Override
40 public String toString() {
41 return "TestJMS{" +
42 "name='" + name + '\'' +
43 ", age=" + age +
44 ", email='" + email + '\'' +
45 '}';
46 }
47 }
以及这是注入到JmsTemplate的MappingJackson2MessageConverter的Bean定义
1 @Bean
2 public MappingJackson2MessageConverter getJacksonMessageConverter() {
3 MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
4 converter.setTargetType(MessageType.TEXT);
5 // 定义了typeId到Class的Map
6 Map<String, Class<?>> typeIdMap = new HashMap<>();
7 typeIdMap.put("TestJMS", TestJMS.class);
8 converter.setTypeIdMappings(typeIdMap);
9 // 设置发送到队列中的typeId的名称
10 converter.setTypeIdPropertyName("TestJMS");
11 converter.setEncoding("UTF-8");
12 return converter;
13 }
通过这样的注入,实现了Class Object格式无须序列化的对象发送与接受
完整的ActiveMQ基于JAVA注解的配置代码如下:
1 import com.test.bean.TestJMS;
2 import org.apache.activemq.ActiveMQConnectionFactory;
3 import org.springframework.context.annotation.Bean;
4 import org.springframework.context.annotation.Configuration;
5 import org.springframework.jms.core.JmsTemplate;
6 import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
7 import org.springframework.jms.support.converter.MessageConverter;
8 import org.springframework.jms.support.converter.MessageType;
9
10 import java.util.Arrays;
11 import java.util.HashMap;
12 import java.util.Map;
13
14 @Configuration
15 public class MQConfig {
16
17 @Bean
18 public ActiveMQConnectionFactory getAMQFactory() {
19 ActiveMQConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory();
20 mqConnectionFactory.setBrokerURL("tcp://59.110.231.87:61616");
21 mqConnectionFactory.setTrustedPackages(Arrays.asList("com.edoctor.bean"));
22 return mqConnectionFactory;
23 }
24
25 @Bean
26 public JmsTemplate getJmsTemplate(ActiveMQConnectionFactory cf, MessageConverter messageConverter) {
27 JmsTemplate jmsTemplate = new JmsTemplate(cf);
28 jmsTemplate.setDefaultDestinationName("EDoctor.JMSTemplate.queue2");
29 jmsTemplate.setMessageConverter(messageConverter);
30 // pubSubDomain = true 为队列模式,false为订阅发布模式
31 jmsTemplate.setPubSubDomain(false);
32 return jmsTemplate;
33 }
34
35 @Bean
36 public MappingJackson2MessageConverter getJacksonMessageConverter() {
37 MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
38 converter.setTargetType(MessageType.TEXT);
39 Map<String, Class<?>> typeIdMap = new HashMap<>();
40 typeIdMap.put("TestJMS", TestJMS.class);
41 converter.setTypeIdMappings(typeIdMap);
42 converter.setTypeIdPropertyName("TestJMS");
43 converter.setEncoding("UTF-8");
44 return converter;
45 }
46
47 }
**四、总结
**
MappingJackson2MessageConverter可以很有效的实现MQ的发送和接受序列化,不需要将POJO手动序列化,实现Serializable接口。基于JSON的解析也顺应主流技术。因为踩了较多的坑,所以特地留此篇博客,如果不对的地方,还希望多多指出。有疑问欢迎留言,谢谢!