在本篇我要做一个spring和Activemq结合的例子。将activemq和spring无缝衔接,并且将activemq的信息持久化到mysql数据库中。这里使用queue
一、依赖的jar,这些是开发的必备jar
A、activemq的jar:activemq-all-5.2.0.jar
B、xbean.jar:用来解析xsd之类的东东
C、xbean-spring-3.1.jar
二、开发步骤
1、用来存储消息的pojo,一定要序列化
package com.hc360.components.jms; import java.io.Serializable; /** * 用来存储我们要发送消息的pojo * */ public class InvokeMessage implements Serializable{ private static final long serialVersionUID = 2L; private String name; private String operate; private String msg; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getOperate() { return operate; } public void setOperate(String operate) { this.operate = operate; } }
2、消息产生器,用来向队列发送消息:Producter
package com.hc360.components.jms; import javax.jms.Queue; import org.springframework.jms.core.JmsTemplate; /** * 发送消息 * @author kongqz */ public class InvokeMessageProducer { private JmsTemplate template; private Queue destination; public void setTemplate(JmsTemplate template) { this.template = template; } public void setDestination(Queue destination) { this.destination = destination; } public void send(InvokeMessage invokeMessage) { template.convertAndSend(this.destination, invokeMessage); } }
3、将pojo转化成可以发送到队列的形式:MessageConverter
package com.hc360.components.jms; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.ObjectMessage; import javax.jms.Session; import org.apache.activemq.command.ActiveMQObjectMessage; import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConverter; /** * @see MessageConverter * 将要发送的pojo转化成activemq可以辨识的类型 */ public class InvokeMessageConverter implements MessageConverter{ //接收消息时候使用 public Object fromMessage(Message msg) throws JMSException, MessageConversionException { if (msg instanceof ObjectMessage) { if (msg instanceof ObjectMessage) { HashMap<String, byte[]> map = (HashMap<String, byte[]>) ((ObjectMessage) msg).getObjectProperty("Map"); try { // POJO must implements Seralizable ByteArrayInputStream bis = new ByteArrayInputStream(map.get("InvokeMessage")); ObjectInputStream ois = new ObjectInputStream(bis); Object returnObject = ois.readObject(); return returnObject; } catch (IOException e) { System.out.println("fromMessage(Message)"); e.printStackTrace(); } catch (ClassNotFoundException e) { System.out.println("fromMessage(Message)"); e.printStackTrace(); } } } else { throw new JMSException("Msg:[" + msg + "] is not Map"); } return null; } //发送消息时候使用 public Message toMessage(Object obj, Session session) throws JMSException, MessageConversionException { if (obj instanceof InvokeMessage) { ActiveMQObjectMessage objMsg = (ActiveMQObjectMessage) session.createObjectMessage(); Map<String, byte[]> map = new HashMap<String, byte[]>(); try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); map.put("InvokeMessage", bos.toByteArray()); } catch (IOException e) { e.printStackTrace(); } objMsg.setObjectProperty("Map", map); return objMsg; } else { throw new JMSException("Object:[" + obj + "] is not InvokeMessage"); } } }
4、消息处理器:Consumer
package com.hc360.components.jms; /** *消费消息 * @author kongqz */ public class InvokeMessageConsumer{ /** * @author Administrator * */ public void printMyOut(InvokeMessage invokeMessage) { System.out.println("等待1秒再处理"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("执行业务操作["+invokeMessage.getName()+"],["+invokeMessage.getOperate()+"],["+invokeMessage.getMsg()+"]"); } }
5、调用jms发送消息,将下边的代码嵌入到相关的触发地。通过我们consumer的延迟处理,我们的队列被处理效果将很容易在控制台看到
try{ for(int i=0;i<200;i++){ //准备发送jms消息 InvokeMessage im = new InvokeMessage(); im.setMsg(i+":有人查询用户列表了!["+new Date()+"]"); im.setName("当前系统通知"); im.setOperate("查询!"); this.getInvokeMessageProducer().send(im); } }catch(Exception e){ System.out.println("发送系统消息出错"); e.printStackTrace(); }
6、相关配置文件(我将内置的activemq5.2服务器直接写入到一个配置文件中,并将相关的jms配置放到一起)
<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.2.0.xsd"> <!-- 推荐版本,使用spring的listenerContainer,消息用数据库持久化保存,服务器重启不会丢失 http://activemq.apache.org/spring-support.html --> <!-- embedded ActiveMQ Broker,内置的ActiveMq服务器 --> <amq:broker useJmx="false" persistent="true"> <amq:persistenceAdapter> <amq:jdbcPersistenceAdapter id="jdbcAdapter" dataSource="#mysql-ds" createTablesOnStartup="true" useDatabaseLock="true"/> <!-- Mysql can setup useDatabaseLock="true",this is defualt HSQLDB,MSSQL plz setup useDatabaseLock="false", if u setup useDatabaseLock="true",u will catch error: MSSQL Error Info:FOR UPDATE clause allowed only for DECLARE CURSOR HSQLDB Error Info:FOR in statement [SELECT * FROM ACTIVEMQ_LOCK FOR UPDATE] see http://www.nabble.com/ActiveMQ-JDBC-Persistence-with-SQL-Server-tf2022248.html#a5560296 --> </amq:persistenceAdapter> <!-- 开发给外部的链接使用tcp方式 --> <amq:transportConnectors> <amq:transportConnector uri="tcp://localhost:0"/> </amq:transportConnectors> </amq:broker> <!-- ActiveMQ connectionFactory --> <amq:connectionFactory id="jmsConnectionFactory" brokerURL="vm://localhost"/> <!-- ActiveMQ destinations,队列名称 --> <amq:queue name="destination" physicalName="org.apache.activemq.spring.Test.spring.embedded"/> <!-- The mysql Datasource that will be used by the Broker --> <bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url"> <value>${jdbc.activemq.url}</value> <!-- mysql version <value>jdbc:mysql://localhost/myproject?relaxAutoCommit=true</value> --> </property> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="poolPreparedStatements" value="true"/> </bean> <!-- Spring JmsTemplate config --> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory"> <!-- lets wrap in a pool to avoid creating a connection per send --> <bean class="org.springframework.jms.connection.SingleConnectionFactory"> <property name="targetConnectionFactory" ref="jmsConnectionFactory"/> </bean> </property> <!-- custom MessageConverter --> <property name="messageConverter" ref="invokeMessageConverter"/> </bean> <!-- invokeMessage converter --> <bean id="invokeMessageConverter" class="com.hc360.components.jms.InvokeMessageConverter"/> <!-- POJO which send Message uses Spring JmsTemplate --> <bean id="invokeMessageProducer" class="com.hc360.components.jms.InvokeMessageProducer"> <property name="template" ref="jmsTemplate"/> <property name="destination" ref="destination"/> </bean> <!-- Message Driven POJO (MDP) --> <bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> <constructor-arg> <bean class="com.hc360.components.jms.InvokeMessageConsumer"> </bean> </constructor-arg> <!-- may be other method --> <property name="defaultListenerMethod" value="printMyOut"/> <!-- custom MessageConverter define --> <property name="messageConverter" ref="invokeMessageConverter"/> </bean> <!-- listener container,MDP无需实现接口 --> <bean id="listenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="jmsConnectionFactory"/> <property name="destination" ref="destination"/> <property name="messageListener" ref="messageListener"/> </bean> </beans>
7、相关的property配置文件(我的持久化jdbc和ssh项目用的jdbc都用单独的配置文件管理)
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/hdssh?useUnicode=true&characterEncoding=UTF-8&jdbcCompliantTruncation=false jdbc.username=root jdbc.password=spring_framework jdbc.activemq.url=jdbc:mysql://localhost:3306/activemq?relaxAutoCommit=true&useUnicode=true&characterEncoding=UTF-8&jdbcCompliantTruncation=false hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.jdbc.batch_size=25 hibernate.jdbc.fetch_size=50 hibernate.show_sql=true hibernate.hbm2ddl.auto=update
三、常见问题处理
1、用mysql的时候无法直接生成相关监控表ACTIVEMQ_ACKS没有创建(mysql5)。这个可能是因为你将表的默认字符集设置为utf-8,如果你使用latin1就不会有这个问题。将activemq的持久化到mysql数据库的时候一定要设定合适的字符集