首先说明,本文不是坑,本文所需源码和项目我会存到我的云盘里,欢迎大家下载和学习。
JMS作为一个可以实现订阅式和队列式的推送消息中间件,一直待web项目中占有一席之地。在普通的Spring整合JMS中,ActiveMQ都是作为一个单独的线程独立启动的,也就意味着,在部署自己开发的项目时,就必须依赖ActiveMQ这个中间件的优先启动。也就是说,如果没有事先启动ActiveMQ这个中间件,自己的项目也就无法正常启动和使用。
由于很多项目并不希望在本项目之外另外跑一个线程,因此本文就将着重讲解如何将支持JMS消息订阅的ActiveMQ整体嵌入到web项目中,当本文结束,就会实现仅仅启动自己开发的项目就可以成功的发送和接受消息。
这个测试案例将使用Spring+JMS+tomcat/weblogic开发,开发工具为myeclipse 10,jdk版本为 6.40.
1.jar包
首先得说一下将ActiveMQ嵌入Spring中所需要的jar包。
这是基本的jar包,必须有,否则项目不能跑起来,
2 .配置文件
首先配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 检查模型 -->
<servlet>
<servlet-name>ClassCheckServlet</servlet-name>
<servlet-class>com.activemq.action.TestJmsService</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ClassCheckServlet</servlet-name>
<url-pattern>login.do</url-pattern>
</servlet-mapping>
</web-app>
web.xml文件servlet跳转和初始化配置,由于我开发的项目是servlet的项目,因此在这里我采用了,如果想实现Spring管理页面跳转,需要在web.xml文件中添加前端控制器代码,本文不做描述,默认大家可以实现,如果不会实现,请移步到我的博客中查找Spring项目搭建相关文章。
按照ActiveMQ官方教程,将MQ嵌入Spring项目其实有好几种实现方式,本文就将使用Spring文件配置形式,达到将MQ嵌入的目的。配置applicationContext.xml文件,此文件我放置在src下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
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-2.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core-5.1.0.xsd
http://activemq.apache.org/camel/schema/spring
http://activemq.apache.org/camel/schema/spring/camel-spring.xsd">
<!-- 配置嵌入式的 activeMQ Broker -->
<amq:broker useJmx="false" persistent="true">
<amq:persistenceAdapter>
<amq:amqPersistenceAdapter directory="activemq-data"
maxFileLength="32mb" />
</amq:persistenceAdapter>
<amq:transportConnectors>
<amq:transportConnector uri="tcp://localhost:61616" />
</amq:transportConnectors>
</amq:broker>
<!-- 配置connectionFactory -->
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>tcp://localhost:61616</value>
</property>
</bean>
</property>
</bean>
<!-- Spring JMS Template -->
<bean id="myJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref local="jmsFactory" />
</property>
</bean>
<!-- 队列 -->
<!-- <amq:queue id="mytestQueue" physicalName="mytest.embedded" />
<amq:queue id="retryMyTestQueue" physicalName="retry.mytest.embedded" /> -->
<!-- 消息订阅模式,在spring xml 里面加,这是发送的 Destination -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<!-- 订阅消息的名字 -->
<constructor-arg index="0" value="orderTopic" />
</bean>
<!-- 生产者 -->
<bean id="mytestProducer" class="com.activemq.jms.MyTestProducer">
<property name="jmsTemplate">
<ref bean="myJmsTemplate" />
</property>
<property name="destination" ref="topicDestination" />
<!-- <property name="retryDestination" ref="retryMyTestQueue" /> -->
</bean>
<!-- 消费者 -->
<!-- <bean id="mytestConsumer" class="com.activemq.jms.MyTestConsumer">
<property name="producer" ref="mytestProducer"></property>
</bean> -->
<!-- Spring 监听器 -->
<bean id="messageListener" class="com.activemq.jms.MyTestConsumer" />
<bean id="listenerContainer"
class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<!-- 工厂 目的地 监听器 这里如果用原始activemq 写,这些属性也是必要的 -->
<property name="connectionFactory">
<ref local="jmsFactory" />
</property>
<property name="destination" ref="topicDestination" />
<property name="messageListener" ref="messageListener" />
</bean>
</beans>
applicationContext.xml文件中的配置有详细的解释,在上面的配置文件中,采用订阅(pub/sub)发布消息模式进行了配置,如果想使用点对点(PTP)形式进行配置,,请将topic相关部分注释掉,并将原先注释掉的代码去掉注释即可。
其实到了这里,已经成功将ActiveMQ嵌入到了项目中,是不是感觉很惊讶?怎么这么简单就嵌入成功了?不信是吧,来,接下来上代码测试。
由于我开发的项目是servlet的项目,因此在这里我采用了使用工具类获取bean,而不是使用Spring注解的形式获取ben ,原理一样,如果想要使用注解方式获取bena的朋友可以在下面联系我。
下面是获取ben的工具类,也是一个发送消息的一个中间载体:
package com.activemq.init;
import java.io.Serializable;
import javax.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.activemq.jms.MyTestProducer;
public class BeansUtil {
/**
* 生产者
*/
private static MyTestProducer myTestProducer;
/**
* 上下文
*/
private static ApplicationContext context;
/**
* 发送Object对象
*
* @param sc
* @param object
*/
public static void sendObject(ServletContext sc, Serializable object) {
getMyTestProducer(sc);
myTestProducer.sendObjectMsg(object);
}
/**
* 获取myTestProducer
*
* @param sc
*/
private static void getMyTestProducer(ServletContext sc) {
context = WebApplicationContextUtils.getWebApplicationContext(sc);
myTestProducer = (MyTestProducer) context.getBean("mytestProducer");
}
}
上面的这个类是一个发送消息的中间载体,起着
下面是一个真正执行发送任务的类,这个类将会把消息以订阅的形式发送到消息中间件中:
package com.activemq.jms;
import java.io.Serializable;
import java.util.Map;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.core.MessagePostProcessor;
/**
*
* @author pengfeiguo
*
* 生产者
*/
public class MyTestProducer {
private JmsTemplate jmsTemplate;
private Destination destination;
private Destination retryDestination;
public void sendObjectMsg(final Serializable object) {
jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createObjectMessage(object);
}
});
}
/*
* 发送文本消息
*/
public void sendTextMsg(Session session, final String msgContent)
throws JMSException {
jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(msgContent);
}
});
}
/*
* 发送MAP类型消息
*/
public void sendMap(Session session, Map<String, Object> map)
throws JMSException {
jmsTemplate.convertAndSend(destination, map,
new MessagePostProcessor() {
public Message postProcessMessage(Message message)
throws JMSException {
// message.setIntProperty("AccountID", 1234);
// message.setJMSCorrelationID("123-00001");
return message;
}
});
}
/**
* @return the jmsTemplate
*/
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
/**
* @param jmsTemplate
* the jmsTemplate to set
*/
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
/**
* @return the destination
*/
public Destination getDestination() {
return destination;
}
/**
* @param destination
* the destination to set
*/
public void setDestination(Destination destination) {
this.destination = destination;
}
/**
* @return the retryDestination
*/
public Destination getRetryDestination() {
return retryDestination;
}
/**
* @param retryDestination
* the retryDestination to set
*/
public void setRetryDestination(Destination retryDestination) {
this.retryDestination = retryDestination;
}
}
下面是一个消息监听类,起着监听发布消息者发布的消息的作用,在这个类中,可以实时监听自己订阅的消息,然后进行下一步处理:
package com.activemq.jms;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;
import com.activemq.entity.User;
public class MyTestConsumer implements MessageListener {
private Message message;
public void onMessage(Message message) {
/**
* 接受文本类型的消息
*/
if (message instanceof TextMessage) { // instanceof
// 测试它所指向的对象是否是TextMessage类
TextMessage text = (TextMessage) message;
try {
System.out.println("发送的文本消息内容为:" + text.getText()); // 接受文本消息
} catch (JMSException e) {
e.printStackTrace();
}
}
/**
* 接受Map类型的消息
*/
if (message instanceof MapMessage) {
MapMessage map = (MapMessage) message;
try {
System.out.println("姓名:" + map.getString("name"));
System.out.println("是否是英雄:" + map.getBoolean("IsHero"));
System.out.println("年龄:" + map.getInt("age"));
} catch (JMSException e) {
e.printStackTrace();
}
}
if (message instanceof ObjectMessage) {
ObjectMessage objMsg = (ObjectMessage) message;
try {
User person = (User) objMsg.getObject();
System.out.println("用户名:" + person.getName() + "年龄:"
+ person.getAge() + "地址:" + person.getSex());
} catch (JMSException e) {
e.printStackTrace();
}
}
setMessage(message);
}
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
}
下面就是一个在web环境中测试的一个action类,此类需要将项目跑起来后,才可以发送消息,
package com.activemq.action;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.activemq.entity.User;
import com.activemq.init.BeansUtil;
public class TestJmsService extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 4811623214895759645L;
// private MyTestProducer mytestProducer;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resp)
throws ServletException, IOException {
String name = request.getParameter("name");
int age = Integer.parseInt(request.getParameter("age"));
String sex = request.getParameter("sex");
User user = new User(name, age, sex);
BeansUtil.sendObject(this.getServletContext(), user);
}
}
下面是index页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
<form method="post" action="login.do">
<table>
<tr>
<td>姓名:</td>
<td><input type="text" name="name" />
</td>
</tr>
<tr>
<td>年龄:</td>
<td><input type="text" name="age" />
</td>
</tr>
<tr>
<td>性别:</td>
<td><input type="text" name="sex" />
</td>
</tr>
</table>
<input type="submit" value="测试" />
</form>
</body>
</html>
值得一说的是,这些基本的东西已经能够完全实现将ActiveMQ嵌入到Spring中,而本文使用的User类,只有性命,年龄、性别,三个属性,因此并没有贴出来,使用jms就不需要再启动项目钱首先启动ActiveMQ了,还有不会的朋友可以在下面的链接中下载我所测试的代码;本文完整源码下载,朋友们也可以前往ActiveMQ官网学习如何将ActiveMQ嵌入Spring中,网址为:ActiveMQ官网嵌入教程,下面是另一篇不错的博客文章:ActiveMQ嵌入项目,另:下面是我在淘宝上看到的JMS教学视频,如果朋友们想要细致的学习JMS,可以移步到这里下载JMS视频:JMS教学视频-淘宝,同时欢迎朋友们加入我的QQ群交流:129113306