018_Spring+ActiveMQ(消息中间件)

1. 配置连接工厂bean

1.1. 我们要想发送消息到ActiveMQ, 就需要创建客户和提供者之间的连接, 连接工厂(ActiveMQConnectionFactory)可以完成这个工作。

1.2. 可以配置spring包下的org.apache.activemq.spring.ActiveMQConnectionFactory

1.3. org.apache.activemq.spring.ActiveMQConnectionFactory类在下面这个jar包中 

1.4. org.apache.activemq.spring.ActiveMQConnectionFactory继承自org.apache.activemq.ActiveMQConnectionFactory 

1.5. 在org.apache.activemq.spring.ActiveMQConnectionFactory中, 可以使用brokerURL属性来指定ActiveMQ的地址和端口号。 

2. 配置目的地对象

2.1. 除了连接工厂外, 我们还需要消息传递的目的地。目的地可以是一个队列, 也可以是一个主题, 这取决于应用的需求。

2.2. 队列模式

2.3. 主题模式 

2.4. 在org.apache.activemq.command.ActiveMQQueue或 org.apache.activemq.command.ActiveMQTopic中, 可以使用physicalName属性来指定队列或主题的名字。 

2.5. 如果消息目的地是队列模式, 也可以不配置(不推荐), 在发送和接收消息的时候指定目的地名称即可。在发送和接收消息的时候设置一个名称, 但它只是一个名称, 它并没有说明你所处理的目的地是什么类型, 如果已经存在该名称的队列或主题的话, 就会使用已有的; 如果尚未存在的话, 将会创建一个新的目的地, 通常会是队列。

3. 配置模板类

3.1. 针对如何消除冗长和重复的JMS代码, Spring给出的解决方案是JmsTemplate。JmsTemplate可以创建连接、获得会话、发送和接收消息。

3.2. 因为JmsTemplate需要知道如何连接到消息提供者, 所以我们必须为connection-Factory属性设置实现了JMS的ConnectionFactory接口的bean引用。 

3.3. 在org.springframework.jms.core.JmsTemplate中, 可以使用defaultDestination属性来指定默认目的地。这样就不必在每次发送和接收消息的时候指定目的地。当然我们这里也可以不必配置目的地, 然后在发送和接收消息的时候指定目的地。 

3.4. org.springframework.jms.core.JmsTemplate类实现了org.springframework.jms.core.JmsOperations接口。 

3.5. 另外, JmsTemplate可以处理所有抛出的笨拙的JMSException异常。如果在使用JmsTemplate时抛出JMSException异常, JmsTemplate将捕获该异常, 然后抛出一个非检查型异常(JMSException都是检查型异常, 因此必须捕获), 该异常是Spring自带的, 是JmsException异常的子类。 

4. 发送消息

4.1. send()方法发送消息, 第一个是消息的目的地(可选参数), 第二个便是具体的消息。我们使用MessageCreator(在这里的实现是作为一个匿名内部类)来构造消息, 在MessageCreator的createMessage()方法中, 有一个session对象, session对象可以创建各种各样的消息(文本消息、流消息、映射消息、对象消息等), 然后返回消息对象。

4.2. JmsTemplate还提供了convertAndSend()方法, 发送消息时, 对消息进行转换。与send()方法不同, convertAndSend()方法并不需要MessageCreator作为参数, 这是因convertAndSend()会使用内置的消息转换器(message converter)为我们创建消息。 

4.3. 在发送之前转换为Message, JmsTemplate内部会进行一些处理, 它使用一个MessageConverter的实现类将对象转换为Message。MessageConverter是Spring定义的接口, 只有两个需要实现的方法: 

4.4. 默认的转换器 

4.5. MappingJackson2MessageConverter: 使用Jackson JSON库实现消息和JSON格式之间的相互转换。

4.6. MarshallingMessageConverter: 使用JAXB库实现消息和XML格式之间的相互转换。

4.7. SimpleMessageConverter: 实现String和TextMessage之间的转换、字节数组和BytesMessage之间的转换、Map和MapMessage之间的转换、Serializable对象和ObjectMessage之间的转换。

4.8. 默认情况下, JmsTemplate在convertAndSend()方法中会使用SimpleMessageConverter。如果你想使用JSON消息的话, 那么可以声明一个MappingJackson2MessageConverter类型的Bean, 然后把它注入到JmsTemplate的messageConverter属性中。

5. 接收消息

5.1. receive()方法接收消息, 它有一个可选的消息目地参数。当调用JmsTemplate的receive()方法时, JmsTemplate会尝试从消息代理中获取一个消息, 如果没有可用的消息, receive()方法会一直等待, 直到获得消息为止。

5.2. 这里有一点需要注意, 当调用message.getText()方法时会抛出JMSException, 这个异常是属于JMS API的。JMSException是一个检查异常, 在JMS操作中会抛出各种各样的JMSException, 但是前面我们使用JmsTemplate时并没有捕获任何JMSException, 是因为JmsTemplate内部已经将需要检查的JMSException转换成了非检查的Spring自己的JmsException。在上面代码中因为调用的是message.getText()方法而不是JmsTemplate的方法, 所以我们需要捕获JMSException。但是按照Spring的设计理念, 我们应该尽量减少检查异常, 所以在catch块里面我们又通过JmsUtils工具把JMSException转换成了非检查的JmsException。

5.3. JmsTemplate同样提供了receiveAndConvert()方法, 在接收消息时, 对消息进行转换。因为使用的是JmsTemplate的方法, 所以我们不需要再捕获JMSException检查异常。这个接收消息代码只有一行有效代码, 非常简洁。

5.4. 不管使用msTemplate的receive()还是receiveAndConvert()方法消费消息, 它们都是同步的, 也就是说接收者在消息到达时需要等待。

6. 异步接收消息

6.1. 要想在消息出现时得到通知, 那么就需要一个监听器监听queue或者topic。Spring提供了以POJO的方式处理消息的能力, 不需要依赖任何接口。在EJB中, message driven bean(MDB)就可以实现异步的处理消息。Spring在这方面参考了EJB3对MDB的实现, 不过在Spring中我们把它称作消息驱动POJO, 也就是message-driven POJO(MDP)。

6.2. 赋予上面POJO接收消息能力的关键在于将其配置成一个Spring消息监听器, Spring的jms命名空间提供了所有相关配置。首先, 我们现需要把上面的POJO对象声明成一个bean。其次, 把MessageHandler变成一个消息驱动POJO, 即把这个bean声明成一个listener。 

7. 异步接收消息-实现MessageListener接口

7.1. MessageListener接口

7.2. 我们的MessageHandler还可以实现一个MessageListener接口, 这样的话就不需要再单独指定消息处理的方法了, MyMessageListener的onMessage()方法会自动被调用。 

7.3. 然后直接配置listener即可(不用再配置method方法属性)。 

8. 对象序列化包名的白名单限制

8.1. ActiveMQ对序列化有包名的白名单限制, 可以把包名加入到配置中, 或者关闭掉白名单限制。

9. SpringJMS发送文本消息例子

9.1. 新建一个名为SpringJMSText的Java项目, 拷入相关jar包。

9.2. 新建JmsQueueSender.java

package com.lywgames.jms;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.jms.core.JmsOperations;
import org.springframework.jms.core.MessageCreator;

public class JmsQueueSender {
	private JmsOperations jmsOperations;
	
    public void setJmsOperations(JmsOperations jmsOperations) {
		this.jmsOperations = jmsOperations;
	}

	public void sendMessage(String message) {
		jmsOperations.send("myspringjmsqueue", new MessageCreator() {
			@Override
			public Message createMessage(Session session) throws JMSException {
				return session.createTextMessage(message);
			}
		});
    }
}

9.3. 新建JmsQueueReceiver.java

package com.lywgames.jms;

import javax.jms.JMSException;
import javax.jms.TextMessage;
import org.springframework.jms.core.JmsOperations;
import org.springframework.jms.support.JmsUtils;

public class JmsQueueReceiver {
private JmsOperations jmsOperations;
	
    public void setJmsOperations(JmsOperations jmsOperations) {
		this.jmsOperations = jmsOperations;
	}
    
    public void receiveMessage() {
    	TextMessage msg = (TextMessage) jmsOperations.receive("myspringjmsqueue");
        try {
        	// 非jms模板抛出的异常
			System.out.println(msg.getText());
		} catch (JMSException e) {
			e.printStackTrace();
			JmsUtils.convertJmsAccessException(e);
		}
    }
}

9.4. 在src目录下配置applicationContext.xml

 9.5. 新建Test.java

package com.lywgames.jms;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	public static void main(String[] args) {
		// 1. 类路径加载配置文件
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		// 2. 获取JmsQueueSender
		JmsQueueSender jmsQueueSender = context.getBean(JmsQueueSender.class);
		// 3. 调用JmsQueueSender的sendMessage方法, 发送消息
		jmsQueueSender.sendMessage("Spring JMS发送文本消息。");
		// 4. 获取JmsQueueReceiver
		JmsQueueReceiver jmsQueueReceiver = context.getBean(JmsQueueReceiver.class);
		// 5. 调用JmsQueueReceiver的receiveMessage方法, 获取消息
		jmsQueueReceiver.receiveMessage();
		// 6. 关闭应用程序上下文
		context.close();
	}
}

9.6. 运行项目效果图

10. SpringJMS发送对象消息例子

10.1. 新建一个名为SpringJMSObject的Java项目, 拷入相关jar包。

10.2. 新建User.java

package com.lywgames.jms;

import java.io.Serializable;

public class User implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private String userName;
    private String password;
    
    public User() {
	}

	public User(String userName, String password) {
		this.userName = userName;
		this.password = password;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [userName=" + userName + ", password=" + password + "]";
	}
    
}

10.3. 新建JmsQueueSender.java

package com.lywgames.jms;

import org.springframework.jms.core.JmsTemplate;

public class JmsQueueSender {
	private JmsTemplate jmsTemplate;
	
    public void setJmsTemplate(JmsTemplate jmsTemplate) {
		this.jmsTemplate = jmsTemplate;
	}

	public void sendMessage(Object message) {
		jmsTemplate.convertAndSend(message);
    }
}

10.4. 新建JmsQueueReceiver.java

package com.lywgames.jms;

import org.springframework.jms.core.JmsTemplate;

public class JmsQueueReceiver {
    private JmsTemplate jmsTemplate;
    
    public void setJmsTemplate(JmsTemplate jmsTemplate) {
		this.jmsTemplate = jmsTemplate;
	}
    
    public void receiveMessage() {
    	User user = (User) jmsTemplate.receiveAndConvert();
        System.out.println(user);
    }
}

10.5. 在src目录下配置applicationContext.xml

10.6. 新建Test.java

package com.lywgames.jms;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	public static void main(String[] args) {
		// 1. 类路径加载配置文件
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		// 2. 获取JmsQueueSender
		JmsQueueSender jmsQueueSender = context.getBean(JmsQueueSender.class);
		// 3. 调用JmsQueueSender的sendMessage方法, 发送消息
		jmsQueueSender.sendMessage(new User("zhangsan", "123456"));
		// 4. 获取JmsQueueReceiver
		JmsQueueReceiver jmsQueueReceiver = context.getBean(JmsQueueReceiver.class);
		// 5. 调用JmsQueueReceiver的receiveMessage方法, 获取消息
		jmsQueueReceiver.receiveMessage();
		// 6. 关闭应用程序上下文
		context.close();
	}
}

10.7. 运行项目效果图

11. SpringJMS消息和JSON格式转换例子

11.1. 新建一个名为SpringJMSJson的Java项目, 拷入相关jar包。

11.2. 新建User.java

package com.lywgames.jms;

import java.io.Serializable;

public class User implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private String userName;
    private String password;
    
    public User() {
	}

	public User(String userName, String password) {
		this.userName = userName;
		this.password = password;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [userName=" + userName + ", password=" + password + "]";
	}
    
}

11.3. 新建Product.java

package com.lywgames.jms;

import java.io.Serializable;

public class Product implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private Integer id;
	private String name;
	
	public Product() {
	}

	public Product(Integer id, String name) {
		this.id = id;
		this.name = name;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

11.4. 新建MyJsonConverter.java

package com.lywgames.jms;

import java.util.HashMap;
import java.util.Map;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageType;

public class MyJsonConverter extends MappingJackson2MessageConverter {
	public MyJsonConverter() {
		// 定义typeId到Class的映射
		Map<String, Class<?>> typeIdMappings = new HashMap<String, Class<?>>();
		typeIdMappings.put(User.class.getName(), User.class);
		typeIdMappings.put(Product.class.getName(), Product.class);
		// 消息是文本类型
		this.setTargetType(MessageType.TEXT);
		// 设置typeId和Class的映射
		this.setTypeIdMappings(typeIdMappings);
		// 发送到目的地的TypeId的名称
//		this.setTypeIdPropertyName(Product.class.getName());
	}
}

11.5. 新建JmsQueueSender.java

package com.lywgames.jms;

import org.springframework.jms.core.JmsTemplate;

public class JmsQueueSender {
	private JmsTemplate jmsTemplate;
	
    public void setJmsTemplate(JmsTemplate jmsTemplate) {
		this.jmsTemplate = jmsTemplate;
	}

	public void sendMessage(Object message, String typeIdPropertyName) {
		MyJsonConverter converter = (MyJsonConverter) jmsTemplate.getMessageConverter();
		converter.setTypeIdPropertyName(typeIdPropertyName);
		jmsTemplate.convertAndSend(message);
    }
}

11.6. 新建JmsQueueReceiver.java

package com.lywgames.jms;

import javax.jms.JMSException;
import org.apache.activemq.command.ActiveMQTextMessage;
import org.springframework.jms.core.JmsTemplate;

public class JmsQueueReceiver {
    private JmsTemplate jmsTemplate;
    
    public void setJmsTemplate(JmsTemplate jmsTemplate) {
		this.jmsTemplate = jmsTemplate;
	}
    
    public void receiveMessage(String typeIdPropertyName) {
        try {
        	MyJsonConverter converter = (MyJsonConverter) jmsTemplate.getMessageConverter();
    		converter.setTypeIdPropertyName(typeIdPropertyName);
    		// 这里不能使用receiveAndConvert()方法
        	ActiveMQTextMessage obj = (ActiveMQTextMessage) jmsTemplate.receive();
			System.out.println(obj.getText());
		} catch (JMSException e) {
			e.printStackTrace();
		}
    }
}

11.7. 在src目录下配置applicationContext.xml

11.8. 新建Test.java

package com.lywgames.jms;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        // 1. 类路径加载配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 2. 获取JmsQueueSender
        JmsQueueSender jmsQueueSender = context.getBean(JmsQueueSender.class);
        // 3. 调用JmsQueueSender的sendMessage方法, 发送消息
        jmsQueueSender.sendMessage(new User("李四", "123456"), User.class.getName());
//      jmsQueueSender.sendMessage(new Product(1001, "牛奶"), Product.class.getName());
        // 4. 获取JmsQueueReceiver
        JmsQueueReceiver jmsQueueReceiver = context.getBean(JmsQueueReceiver.class);
        // 5. 调用JmsQueueReceiver的receiveMessage方法, 获取消息
        jmsQueueReceiver.receiveMessage(User.class.getName());
        // 6. 关闭应用程序上下文
        context.close();
    }
}

11.9. 运行项目效果图

12. SpringJMS异步接收消息例子

12.1. 新建一个名为SpringJMSMap的Java项目, 拷入相关jar包。

12.2. 新建JmsQueueSender.java

package com.lywgames.jms;

import org.springframework.jms.core.JmsTemplate;

public class JmsQueueSender {
    private JmsTemplate jmsTemplate;
    
    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendMessage(Object message) {
        jmsTemplate.convertAndSend(message);
    }
}

12.3. 新建AnsyJmsQueueReceiver.java

package com.lywgames.jms;

import java.util.Map;

public class AnsyJmsQueueReceiver {
    public void handleMessage(Map<String, Object> message){
        System.out.println(message);
    }
}

12.4. 在src目录下配置applicationContext.xml

12.5. 新建Test.java

package com.lywgames.jms;

import java.util.HashMap;
import java.util.Map;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        // 1. 类路径加载配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 2. 获取JmsQueueSender
        JmsQueueSender jmsQueueSender = context.getBean(JmsQueueSender.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", 100000000000L);
        map.put("name", "lisi");
        // 3. 调用JmsQueueSender的sendMessage方法, 发送消息
        jmsQueueSender.sendMessage(map);
        // 4. 关闭应用程序上下文
        context.close();
    }
}

12.6. 运行项目效果图 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值