业务场景:
需要从某集团服务器SqlServer数据库中获取数据进行转换,并插入到我司开发服务器Mysql数据库中。过渡期项目使用Spring Boot 与MyBatis整合进行数据抓取、转换与创建。
Spring Boot 版本:2.1.3.RELEASE
MyBatis-Spring Boot整合包版本:1.3.2
mysql驱动包版本:5.1.43
sqlserver驱动包版本:3.0
连接池:HicariCP
maven (阿里):
<!-- spring boot 启动引导包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- spring boot jdbc支持包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mybatis-spring boot整合包 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<!-- sqlserver 数据库驱动包 -->
<dependency>
<groupId>com.microsoft</groupId>
<artifactId>sqljdbc4</artifactId>
<version>${sqlserver.driver.version}</version>
</dependency>
<!-- mysql 数据库驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
application.yml配置文件(或application.properties)
注:使用自定义数据源取代默认配置的数据源时,yml(properties)中的默认url一项需要改变为jdbc-url
spring:
datasource:
mysql:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://xxx.xxx.xxx.xxx:xxxx/xxx?useUnicode=true&characterEncoding=utf8&useSSL=false&autoReconnect=true
username: xxxxx
password: xxxxx
sqlserver:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc-url: jdbc:sqlserver://xxx.xxx.xxx.xxx:xxxx;Database=
username: xxxxx
password: xxxxx
config:
1.MysqlDataSourceConfigurer
@Configuration
@MapperScan(basePackages = "com.logwsd.delsync.dao.mysql",sqlSessionTemplateRef = "mysqlSqlSessionTemplate")
public class MysqlDataSourceConfigurer {
@Bean(name="mysqlDataSource")
@ConfigurationProperties(prefix = "spring.datasource.mysql")
@Primary
public DataSource mysqlDataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name = "mysqlSqlSessionFactory")
public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource")DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:**mapper/*.xml"));
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
@Bean(name="mysqlSqlSessionTemplate")
public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mysqlSqlSessionFactory")SqlSessionFactory factory){
return new SqlSessionTemplate(factory);
}
}
2.SqlServerDataSourceConfigurer
@Configuration
@MapperScan(basePackages = "com.logwsd.delsync.dao.sqlserver",sqlSessionTemplateRef = "sqlServerSqlSessionTemplate")
public class SqlServerDataSourceConfigurer {
@Bean("sqlServerDataSource")
@ConfigurationProperties(prefix = "spring.datasource.sqlserver")
public DataSource sqlServerDataSource() {
return DataSourceBuilder.create().build();
}
@Bean("sqlServerSqlSessionFactory")
public SqlSessionFactory sqlserverSqlSessionFactory(@Qualifier("sqlServerDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(sqlServerDataSource());
return sqlSessionFactoryBean.getObject();
}
@Bean("sqlServerSqlSessionTemplate")
public SqlSessionTemplate sqlserverSqlSessionTemplate(@Qualifier("sqlServerSqlSessionFactory") SqlSessionFactory factory){
return new SqlSessionTemplate(factory);
}
}
注:此处Mysql数据源使用的是*Mapper.xml形式的mapper,SqlServer使用的是注解式mapper。配置类的区别在于
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:**mapper/*.xml"));
两句,并且在配置多数据源的场景下必须给某数据源设置@Primary注解。
包路径:
关于数据迁移:
过度阶段使用该种形式,在向mq生产消费模式转型,有考虑使用阿里cannel框架进行伪装的主从复制,相关内容后续更新。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2019-06-06:
该项目进行重构,进行重构总结。
数据源由先前的SqlServer改为IBM-MQ(基于JMS消息协议),队列中传递河北厂与华北厂的SAP数据消息,消息体为JMSByteMessage。通过获取bytes组装成String,String内容为XML,后将XML解析为JSON再供给后续进行处理。
XML解析工具类是借用编写好的工具类,非本人编写,后续会贴出代码。
根据工厂代码识别并对消息进行分发,采用RabbitMQ进行消息传递,根据不同的场景选用不同的RabbitTemplate发送消息,供下一级消费。
代码设计采用二级生产者与消费者的线程模型,IBM-MQ生产出的消息由我方接收,接受后进行消息解析与消息组装,组装后的消息加入到阻塞队列中,由消费线程进行消费处理。消费线程负责将,消息进行工厂级别的区分,进行实体的匹配与组装,再按照工厂进行消息派发。
多数据源:
本项目设计到两种多数据源场景,一种为上一版本的程序中涉及到多个不同种、不同表的数据源,另一种为本次重构中涉及到的同种类型、分库分表或主从集群的数据源。后者实现思路:首先向Spring 容器中注入多个数据源(DataSource),然后使用AbstractRoutingDataSource(后作ARDS)的实现类进行多个数据源的集成,ARDS中封装有Map<Object,Object>。key为查找数据源的键,可以用常量或枚举或其他手段进行区分;value为数据源对象,在调用数据库操作前可以通过ARDS的determineCurrentLookupKey方法返回值以确定使用特定数据源。在整合MyBatis时将ARDS的实现类作为DataSource设置入SqlSessionFactoryBean,后续操作与MyBatis配置类无异。
代码如下:
继承了ARDS的动态数据源类:
public class LogwsdDynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return LogwsdOrderGenerateHandler.get();
}
}
数据源-Mybatis配置类
@Configuration
@MapperScan(basePackages = "com.logwsd.ibmmqreceiver.dao.logwsd",sqlSessionTemplateRef = "logwsdSqlSessionTemplate")
public class LogwsdDataSourceConfigurer {
@Bean("hebeiMysqlDataSource")
@ConfigurationProperties(prefix = "spring.datasource.mysql.hebei")
public DataSource hebeiMysqlDataSource(){
return DataSourceBuilder.create().build();
}
@Bean("huabeiMysqlDataSource")
@ConfigurationProperties(prefix = "spring.datasource.mysql.huabei")
public DataSource huabeiMysqlDataSource(){
return DataSourceBuilder.create().build();
}
@Bean("logwsdDynamicDataSource")
public LogwsdDynamicRoutingDataSource logwsdDynamicRoutingDataSource(@Qualifier("hebeiMysqlDataSource") DataSource hebeiDataSource ,@Qualifier("huabeiMysqlDataSource") DataSource huabeiDataSource){
LogwsdDynamicRoutingDataSource dynamicRoutingDataSource = new LogwsdDynamicRoutingDataSource();
Map<Object,Object> dataSources = new HashMap<>();
//设置默认的数据源
dynamicRoutingDataSource.setDefaultTargetDataSource(hebeiDataSource);
//添加河北和华北厂的数据源
dataSources.put(DataSourceKey.HEBEI_DATASOURCE,hebeiDataSource);
dataSources.put(DataSourceKey.HUABEI_DATASOURCE,huabeiDataSource);
dynamicRoutingDataSource.setTargetDataSources(dataSources);
return dynamicRoutingDataSource;
}
@Bean("logwsdSqlSessionFactory")
public SqlSessionFactory logwsdDynamicDataSourceSqlSessionFactoryBean(@Qualifier("logwsdDynamicDataSource")LogwsdDynamicRoutingDataSource dynamicRoutingDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicRoutingDataSource);
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = pathMatchingResourcePatternResolver.getResources("classpath:mapper/*Mapper.xml");
sqlSessionFactoryBean.setMapperLocations(resources);
return sqlSessionFactoryBean.getObject();
}
@Bean("logwsdSqlSessionTemplate")
public SqlSessionTemplate logwsdSqlSessionTemplate(@Qualifier("logwsdSqlSessionFactory")SqlSessionFactory factory){
return new SqlSessionTemplate(factory);
}
@Bean("transactionManager")
public PlatformTransactionManager platformTransactionManager(@Qualifier("logwsdDynamicDataSource") LogwsdDynamicRoutingDataSource dynamicRoutingDataSource){
return new DataSourceTransactionManager(dynamicRoutingDataSource);
}
}
选择数据源:
public void handle(SourceMessageWrapper mw) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
ZssdhAnalyzerJSONImpl zssdhAnalyzer = new ZssdhAnalyzerJSONImpl(mw);
ZssdiAnalyzerJSONImpl zssdiAnalyzer = new ZssdiAnalyzerJSONImpl(mw);
ZvttkAnalyzerJSONImpl zvttkAnalyzer = new ZvttkAnalyzerJSONImpl(mw);
List<Zssdh> zssdhList = zssdhAnalyzer.analy();
boolean flag = false;
if (zssdhList != null && zssdhList.size() > 0 && (MappingConstant.HEBEI_DOC_SYMBOL.equals(zssdhList.get(0).getVkorg()) || MappingConstant.HUABEI_DOC_SYMBOL.equals(zssdhList.get(0).getVkorg()))) {
//选用河北厂数据源
if(MappingConstant.HEBEI_DOC_SYMBOL.equals(zssdhList.get(0).getVkorg())) {
LogwsdOrderGenerateHandler.setRouter(DataSourceKey.HEBEI_DATASOURCE);
flag = true;
}
//选用华北厂数据源
if(MappingConstant.HUABEI_DOC_SYMBOL.equals(zssdhList.get(0).getVkorg())){
LogwsdOrderGenerateHandler.setRouter(DataSourceKey.HUABEI_DATASOURCE);
flag = true;
}
if(flag) {
List<Zssdi> zssdiList = zssdiAnalyzer.analy();
List<Zvttk> zvttkList = zvttkAnalyzer.analy();
try {
logwsdOrderGenerateHandler.handle(zvttkList, zssdhList, zssdiList);
} finally {
//清除设置的数据源标识
LogwsdOrderGenerateHandler.clear();
}
}
}
if(!flag){
throw new UnknownOrganizationException();
}
}
设置/取出数据源标识:
public static ThreadLocal<DataSourceKey> router = new ThreadLocal();
public static void setRouter(DataSourceKey key){
router.set(key);
}
public static DataSourceKey get(){
return router.get();
}
public static void clear(){
router.remove();
}
数据源标识:
public enum DataSourceKey {
HEBEI_DATASOURCE,
HUABEI_DATASOURCE
}
数据分析:
XML -> JSON 转换工具类:该工具核心部分来源自网络,感谢原作者。
第三方jar涉及到fastjson与DOM4J
private Logger logger = LoggerFactory.getLogger(this.getClass());
public Document parseStringToXMlObject(String xmlContent) throws DocumentException {
return DocumentHelper.parseText(xmlContent);
}
public JSONObject xmltoJson(String xml) throws DocumentException {
JSONObject jsonObject = new JSONObject();
Document document = this.parseStringToXMlObject(xml);
//获取根节点元素对象
Element root = document.getRootElement();
iterateNodes(root, jsonObject);
return jsonObject;
}
private void iterateNodes(Element node,JSONObject json){
//获取当前元素的名称
String nodeName = node.getName();
//判断已遍历的JSON中是否已经有了该元素的名称
if(json.containsKey(nodeName)){
//该元素在同级下有多个
Object Object = json.get(nodeName);
JSONArray array = null;
if(Object instanceof JSONArray){
array = (JSONArray) Object;
}else {
array = new JSONArray();
array.add(Object);
}
//获取该元素下所有子元素
List<Element> listElement = node.elements();
if(listElement.isEmpty()){
//该元素无子元素,获取元素的值
String nodeValue = node.getTextTrim();
array.add(nodeValue);
json.put(nodeName, array);
return ;
}
//有子元素
JSONObject newJson = new JSONObject();
//遍历所有子元素
for(Element e:listElement){
//递归
iterateNodes(e,newJson);
}
array.add(newJson);
json.put(nodeName, array);
return ;
}
//该元素同级下第一次遍历
//获取该元素下所有子元素
List<Element> listElement = node.elements();
if(listElement.isEmpty()){
//该元素无子元素,获取元素的值
String nodeValue = node.getTextTrim();
json.put(nodeName, nodeValue);
return ;
}
//有子节点,新建一个JSONObject来存储该节点下子节点的值
JSONObject object = new JSONObject();
//遍历所有一级子节点
for(Element e:listElement){
//递归
iterateNodes(e,object);
}
json.put(nodeName, object);
return ;
}
实体映射处理
abtract class JSONAnalyzarImpl
private Logger logger = LoggerFactory.getLogger(this.getClass());
private FileOutputUtil fileOutputUtil = new FileOutputUtil("jsonAnalyzerErr.log");
protected JSONObject jsonObject;
public JSONAnalyzerImpl(SourceMessageWrapper sourceMessageWrapper){
super(sourceMessageWrapper);
this.jsonObject = sourceMessageWrapper.getJsonObject();
}
public JSONAnalyzerImpl(){}
//获取特定节点数据,该数据可能为JSONArray也可能为JSONObject,统一转化为JSONArray
protected JSONArray getJsonArrayByKey(String key){
SourceMessageWrapper mw = this.sourceMessageWrapper;
JSONObject jsonObject = mw.getJsonObject();
String[] prefixs = MappingConstant.SAP_SOURCE_MESSAGE_PREFIX.split("\\.");
for(String prefix : prefixs){
jsonObject = (JSONObject)jsonObject.get(prefix);
}
JSON json = (JSON) jsonObject.get(key);
if(json instanceof JSONObject){
List list = new LinkedList();
list.add(json);
JSONArray array = new JSONArray(list);
return array;
}
return (JSONArray) json;
}
//使用反射处理字段映射,此处字段名与JSON节点名内容一致,大小写不同
protected List<Object> analy(String nodeName,Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
JSONArray array = getJsonArrayByKey(nodeName);
if (array == null || array.size() == 0) {
throw this.throwException();
}
List<Object> objectList = new LinkedList<>();
Iterator itr = array.iterator();
while (itr.hasNext()) {
JSONObject jsonObject = new JSONObject((Map<String, Object>) itr.next());
Object obj = clazz.getDeclaredConstructor().newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
if(BigDecimal.class.getName().equals(field.getType().getName())){
field.set(obj,new BigDecimal(jsonObject.getString(field.getName().toUpperCase())));
continue;
}
if(Timestamp.class.getName().equals(field.getType().getName())){
String t = jsonObject.getString(jsonObject.getString(field.getName().toUpperCase()));
if("0000-00-00".equals(t)){
t = "1970-01-01 00:00:00";
}
field.set(obj,Timestamp.valueOf(t));
continue;
}
if(Date.class.getName().equals(field.getType().getName())){
String t = jsonObject.getString(jsonObject.getString(field.getName().toUpperCase()));
if("0000-00-00".equals(t)){
t = "1970-01-01";
}
field.set(obj,Date.valueOf(t));
continue;
}
if(Integer.class.getName().equals(field.getType().getName())){
String content = (String)jsonObject.get(field.getName());
if(content != null) {
field.set(obj, Integer.valueOf(content));
}
continue;
}
field.set(obj, jsonObject.get(field.getName().toUpperCase()));
} catch (IllegalAccessException e) {
// e.printStackTrace();
logger.error("while["+field.getName()+"] value setting process throws Exception,value should be"+jsonObject.get(field.getName())+"\nerror message => "+e.getMessage());
logger.error("now start persistence");
try{
fileOutputUtil.fileOutput((String) jsonObject.get(field.getName()),array.toString());
}catch(Exception e1){
logger.error("persist failed");
}
}
field.setAccessible(false);
}
objectList.add(obj);
}
return objectList;
}
//此处为模板方法,子类实现后抛出特定异常
protected abstract SourceMessagePartInfoNullException throwException();
class ZssdhAnalyzer (另外两中实体分析转换类同下)
public class ZssdhAnalyzerJSONImpl extends JSONAnalyzerImpl {
private Logger logger = LoggerFactory.getLogger(this.getClass());
public ZssdhAnalyzerJSONImpl(SourceMessageWrapper sourceMessageWrapper) {
super(sourceMessageWrapper);
}
@Override
protected SourceMessagePartInfoNullException throwException() {
throw new ZssdhInfoNullException();
}
//转换返回值范型类型
public List<Zssdh> analy() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
List<Object> sapZssdh = super.analy("SapZssdh", Zssdh.class);
List<Zssdh> zssdhList = new LinkedList<>();
for(Object object : sapZssdh){
zssdhList.add((Zssdh)object);
}
return zssdhList;
}
}
消息接收与进一步的生产消费
JMS的消息接收,自定义接收类,继承MessageListenerAdapter夫类,
重写void onMessage(Message message,@Notnull Session session) 方法
@Component
public class SourceMessageRecevierImpl extends MessageListenerAdapter implements ISourceMessageReceiver{
private Logger logger = LoggerFactory.getLogger(this.getClass());
private AtomicInteger counter = new AtomicInteger(0);
@Autowired
private MessageManager messageManager;
@Autowired
private XMLUtil xmlUtil;
@Override
@JmsListener(destination = "×××××××××××")
public void onMessage(Message message, @NotNull Session session) throws JMSException{
logger.info("accept message " + counter.incrementAndGet());
JMSBytesMessage sourceMessage = (JMSBytesMessage) message;
byte[] container = new byte[(int) sourceMessage.getBodyLength()];
sourceMessage.readBytes(container);
String messageBody = new String(container);
SourceMessageWrapper mw = null;
boolean flag = false;
try {
JSONObject object = xmlUtil.xmltoJson(messageBody);
mw = new SourceMessageWrapper(MappingConstant.JSON_TYPE_MESSAGE,object);
} catch (Exception e) {
logger.error("error : {} , {}",e.getMessage(),e.getStackTrace());
logger.error("XML -> JSON process failed , trying attempt to process XML");
flag = !flag;
}
if(flag){
try {
//Document document = xmlUtil.parseStringToXMlObject(messageBody);
//mw = new SourceMessageWrapper(MappingConstant.XML_TYPE_MESSAGE,document);
throw new DocumentException("xml producer developing");
} catch (DocumentException e) {
logger.error("parsing XML process failed , persist source message");
flag = !flag;
}
if(flag){
//process strategy failed : persist sourceMessage
logger.error("--------persist message--------");
logger.error("message body => \n" + messageBody + "\n");
logger.error("--------persist finished-------");
return ;
}
}
messageManager.addMessage(mw);
}
}
注:因赶项目进度JSON处理失败的XML适配并没有完成,后续进行完善
消息载体 SourceMessageWrapper
public class SourceMessageWrapper {
private String type;
private JSONObject jsonObject;
private Document xmlObject;
}
MessageManager 该管理器持有消息的阻塞队列,长度规定为200
onMessage包装出的消息将添加进阻塞队列,规定消费线程进行消费
@Component
public class MessageManager {
public MessageManager(){}
private BlockingQueue<SourceMessageWrapper> queue = new LinkedBlockingQueue<>(200);
public void addMessage(SourceMessageWrapper message){
queue.add(message);
}
public SourceMessageWrapper getMessage() throws InterruptedException {
return this.queue.take();
}
}
消费线程进行消费,若获取不到消息则进行阻塞,当队列添加消息时会队列会唤醒线程
线程任务为内部类
public class MessageConsumerHolder implements InitializingBean {
private ExecutorService executorService = null;
@Autowired
private MessageManager messageManager;
@Autowired
private DBPersistHandler dbPersistHandler;
private Logger logger = LoggerFactory.getLogger(this.getClass());
public MessageConsumerHolder(){
executorService = new ThreadPoolExecutor(MappingConstant.CORE_THREAD_SIZE,MappingConstant.MAX_THREAD_SIZE,100, TimeUnit.MILLISECONDS,new SynchronousQueue<>(true));
}
public void init(){
for(int i = 0 ; i < MappingConstant.MAX_THREAD_SIZE ; ++i){
executorService.submit(new MessageConsumer(messageManager));
}
}
//根据配置进行消费者线程池任务的初始化
@Override
public void afterPropertiesSet() throws Exception {
init();
}
private class MessageConsumer implements Runnable {
private MessageManager messageManager;
private Logger logger = LoggerFactory.getLogger(this.getClass());
MessageConsumer(MessageManager messageManager){
this.messageManager = messageManager;
}
@Override
public void run() {
while(true){
SourceMessageWrapper mw = null;
try {
mw = messageManager.getMessage();
} catch (InterruptedException e) {
logger.error("interruptedException {} \n {}",e.getMessage(),e.getStackTrace());
continue;
}catch(Exception e){
logger.error("msg" + e.getMessage());
}
try {
dbPersistHandler.handle(mw);
} catch (Exception e){
logger.error(e.getMessage());
}
}
}
}
}
MQ配置(IBM-MQ、RabbitMQ)
@Configuration
public class HebeiIBMMQConnectorConfigurer {
@Value("${hb.ibm.mq.host}")
private String host;
@Value("${hb.ibm.mq.port}")
private Integer port;
@Value("${hb.ibm.mq.queue-manager}")
private String queueManager;
@Value("${hb.ibm.mq.channel}")
private String channel;
@Value("${hb.ibm.mq.username}")
private String username;
@Value("${hb.ibm.mq.password}")
private String password;
@Value("${hb.ibm.mq.receive-timeout}")
private long receiveTimeout;
@Bean
public MQQueueConnectionFactory mqQueueConnectionFactory() {
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName(host);
try {
mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
mqQueueConnectionFactory.setCCSID(1208);
mqQueueConnectionFactory.setChannel(channel);
mqQueueConnectionFactory.setPort(port);
mqQueueConnectionFactory.setQueueManager(queueManager);
} catch (Exception e) {
e.printStackTrace();
}
return mqQueueConnectionFactory;
}
@Bean
UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter(MQQueueConnectionFactory mqQueueConnectionFactory) {
UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter = new UserCredentialsConnectionFactoryAdapter();
userCredentialsConnectionFactoryAdapter.setUsername(username);
userCredentialsConnectionFactoryAdapter.setPassword(password);
userCredentialsConnectionFactoryAdapter.setTargetConnectionFactory(mqQueueConnectionFactory);
return userCredentialsConnectionFactoryAdapter;
}
@Bean
@Primary
public CachingConnectionFactory cachingConnectionFactory(UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter) {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
cachingConnectionFactory.setTargetConnectionFactory(userCredentialsConnectionFactoryAdapter);
cachingConnectionFactory.setSessionCacheSize(500);
cachingConnectionFactory.setReconnectOnException(true);
return cachingConnectionFactory;
}
@Bean
public PlatformTransactionManager jmsTransactionManager(CachingConnectionFactory cachingConnectionFactory) {
JmsTransactionManager jmsTransactionManager = new JmsTransactionManager();
jmsTransactionManager.setConnectionFactory(cachingConnectionFactory);
return jmsTransactionManager;
}
@Bean
public JmsOperations jmsOperations(CachingConnectionFactory cachingConnectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate(cachingConnectionFactory);
jmsTemplate.setReceiveTimeout(receiveTimeout);
return jmsTemplate;
}
}
RabbitMQ
@Configuration
public class LogwsdHuabeiRabbitMQConfigurer {
@Value("${spring.rabbitmq.huabei.host}")
private String host;
@Value("${spring.rabbitmq.huabei.port}")
private int port;
@Value("${spring.rabbitmq.huabei.username}")
private String username;
@Value("${spring.rabbitmq.huabei.password}")
private String password;
public static final String EXCHANGE_A = "my-mq-exchange_A";
public static final String QUEUE_A = "QUEUE_A";
public static final String ROUTINGKEY_A = "spring-boot-routingKey_A";
@Bean("huabeiMQConnectionFactory")
public ConnectionFactory huabeiMQConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host,port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost("/");
connectionFactory.setPublisherConfirms(true);
return connectionFactory;
}
@Bean("huabeiRabbitTemplate")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
//必须是prototype类型
public RabbitTemplate huabeiRabbitTemplate(@Qualifier("huabeiMQConnectionFactory") ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
return template;
}
@Bean("huabeiMQdefaultExchange")
public DirectExchange defaultExchange() {
return new DirectExchange(EXCHANGE_A);
}
@Bean("huabeiMQqueueA")
public Queue queueA() {
//队列持久
return new Queue(QUEUE_A, true);
}
@Bean("huabeiMQbinding")
public Binding binding(@Qualifier("huabeiMQdefaultExchange") DirectExchange defaultExchange ,@Qualifier("huabeiMQqueueA") Queue queueA) {
return BindingBuilder.bind(queueA).to(defaultExchange).with(ROUTINGKEY_A);
}
}