Spring利用AOP 自定义注解实现数据源动态切换

使用到了AbstractRoutingDataSource,它是一个数据源抽象类,它继承于AbstractDataSource,而AbstractDataSource实现DataSource接口。这个抽象的数据源类,维护了一个目标数据源表,一个默认的数据源,有一个抽象的方法用来提供数据源key,每次使用数据源获取连接的时候,都会使用数据源key去数据源表查找对应数据源返回,找不到就返回默认的数据源,因此我们想要切换数据源,只需要继承这个抽象的数据源类,提供获取数据源key的方法即可。我们可以创建一个上下文数据源key维护类,它有一个ThreadLocal字段维护上下文数据源key,自定义数据源类每次获取数据源key时候都在上下文数据源维护类的ThreadLocal里获取。接下来配置好多个数据源,并配置我们的自定义数据源bean,指定数据源表和默认数据源,并且SqlSessionFactory和事务管理类都使用这个数据源bean。最后就是利用aop,把上下文数据源key维护类的数据源key切换逻辑织入代码。我们还需要一个自定义注解,在对应的业务代码上标注对应的数据源,aop前置增强获取注解值,保存到上下文数据源key维护类的threadlocal里,后置增强在threadlocal中移除,实现数据源key动态切换,最终实现数据源动态切换。还有就是数据源切换的代码要在事务代码之前织入,这样事务才能生效,因此数据源切换aop的order需要小于事务管理的order。

代码基础,参考文章《SpringMVC 使用AOP添加日志》

核心代码:

自定义数据源类

package cn.tnt.aop.datasource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 自定义数据源
 * @author WQ
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource{
	private static final Logger log=LoggerFactory.getLogger(DynamicDataSource.class);
	
	/**
	 * 重写获取DataSource的key的方法
	 * 数据源每次获取连接时候调用该方法得到数据源对应的key
	 * 当key为null将使用默认数据源
	 */
	@Override
	protected Object determineCurrentLookupKey() {
		String key = DynamicDatasourceHolder.getDataSourceKey();
		log.info("数据源对应的key设置为:"+key);
		return key;
	}

}

上下文数据源key维护类

package cn.tnt.aop.datasource;


/**
 * 上下文数据源key维护类
 * @author WQ
 *
 */
public class DynamicDatasourceHolder {
	private static ThreadLocal<String> holder=new ThreadLocal<String>();
	
	/**
	 * 设置上下文数据源对应的key
	 * @param key
	 */
	public static void setDataSourceKey(String key){
		holder.set(key);
	}
	
	/**
	 * 获取上下文数据源对应的key
	 * @return
	 */
	public static String getDataSourceKey(){
		return holder.get();
	}
	
	/**
	 * 清除上下文数据源key
	 */
	public static void removeDataSourceKey(){
		holder.remove();
	}
}

自定义数据源类只有一个方法,获取数据源key,在每次使用数据源获取连接的时候都会执行这个方法,使用这个key去数据源表(Map)里去查找数据源,找不到返回默认数据源,找到使用这个数据源。所以我们只需要动态提供数据源key即可。key的获取来自于上下文数据源key维护类,线程安全使用ThreadLocal来保存。

spring配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
		
	<context:component-scan base-package="cn.tnt.aop.*" />
<!-- 	<context:component-scan base-package="cn.tnt.aop.service,cn.tnt.aop.mapper,cn.tnt.aop.datasource" /> -->
	<!-- 启动AspectJ支持 -->
	<aop:aspectj-autoproxy proxy-target-class="true" />
	
    
    <bean id="msgDatasource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://10.*.*.*:3306/message" />
        <property name="username" value="root" />
        <property name="password" value="sinochem" />
    </bean>
    <bean id="goodsDatasource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://10.*.*.*:3306/sinochem_oil" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
    </bean>
    
    <bean id="dataSource" class="cn.tnt.aop.datasource.DynamicDataSource">
    	<property name="targetDataSources">
    		<map key-type="java.lang.String">
    			<entry key="msgDatasource" value-ref="msgDatasource"/>
    			<entry key="goodsDatasource" value-ref="goodsDatasource"/>
    		</map>
    	</property>
    	<property name="defaultTargetDataSource" ref="msgDatasource"></property>
    </bean>    
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="typeAliasesPackage" value="cn.tnt.aop.entity"></property>
        <property name="mapperLocations" value="classpath*:cn/tnt/aop/mapper/*Mapper.xml"></property>
    </bean>
    
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
        <property name="basePackage" value="cn.tnt.aop.mapper"></property>
    </bean>
    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- aop数据源的切换植入要在事物开启之前,否则事物可能会失效 -->
    <tx:annotation-driven transaction-manager="transactionManager" order="2"/>
</beans>

配置里配置了两个数据源和自定义数据源,自定义数据源的数据源表和默认数据源都需要配置,SqlSessionFactory和事物管理都使用自定义数据源。事物管理的order设置为2,在后面的数据源切换切面类order值为1,order值越小优先级越高,保证数据源切换代码在事物代码之前执行,从而保证事物生效。

自定义注解

package cn.tnt.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface DynamicDataSourceAnnotation {
	String dataSource() default "msgDatasource";
}

 

数据源切面类

package cn.tnt.aop.datasource;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import cn.tnt.aop.annotation.DynamicDataSourceAnnotation;

@Component
@Aspect
@Order(1)
public class DataSourceAspect {
	private static final Logger blog=LoggerFactory.getLogger("cn.tnt.aop.datasource.DataSourceAspect.sourceBefore");
	private static final Logger alog=LoggerFactory.getLogger("cn.tnt.aop.datasource.DataSourceAspect.sourceAfter");
	
	/**
	 * 前置增强
	 * 获取注解中的数据源key
	 * 把key设置到上下文中,以供数据源Map根据这个key找到对应的数据源
	 * @param point
	 */
	@Before("@annotation(cn.tnt.aop.annotation.DynamicDataSourceAnnotation)") //匹配有这个注解的方法
	public void sourceBefore(JoinPoint point){
		Class<? extends Object> targetClass = point.getTarget().getClass();
		DynamicDataSourceAnnotation dynamicDataSourceAnnotation = targetClass.getAnnotation(DynamicDataSourceAnnotation.class);
		if(dynamicDataSourceAnnotation!=null){ //类上标记该注解
			String methodName = point.getSignature().getName(); //方法名称
			Class[] parameterTypes = ((MethodSignature)point.getSignature()).getParameterTypes(); //方法参数类型列表
			blog.info("========== "+targetClass.getName()+"."+methodName+" ==========");
			Method method=null;
			try {
				 method = targetClass.getMethod(methodName, parameterTypes); //获取方法
			} catch (Exception e) {
				blog.error("========== sourceBefore get method error ==========" ,e);
			} 
			if(method!=null && method.isAnnotationPresent(DynamicDataSourceAnnotation.class)){
				DynamicDataSourceAnnotation da = method.getAnnotation(DynamicDataSourceAnnotation.class);
				String dataSource = da.dataSource();
				DynamicDatasourceHolder.setDataSourceKey(dataSource);
				blog.info("========== 	set dataSource "+dataSource+" ==========");
			}
		}
		
	}
	
	/**
	 * 后置增强
	 * 获取注解值,在上下文中清除这个值
	 * @param point
	 */
	@After("@annotation(cn.tnt.aop.annotation.DynamicDataSourceAnnotation)") //匹配有这个注解的方法
	public void sourceAfter(JoinPoint point){
		Class<? extends Object> targetClass = point.getTarget().getClass();
		DynamicDataSourceAnnotation dynamicDataSourceAnnotation = targetClass.getAnnotation(DynamicDataSourceAnnotation.class);
		if(dynamicDataSourceAnnotation!=null){  //类上标记该注解
			String methodName=point.getSignature().getName(); //方法名称
			Class[] parameterTypes = ((MethodSignature)point.getSignature()).getParameterTypes(); //方法参数类型列表
			alog.info("========== "+targetClass.getName()+"."+methodName+" ==========");
			Method method=null;
			try{
				method=targetClass.getMethod(methodName, parameterTypes);
			}catch(Exception e){
				alog.error("========== sourceAfter get method error ==========" ,e);
			}
			if(method!=null && method.isAnnotationPresent(DynamicDataSourceAnnotation.class)){
				DynamicDataSourceAnnotation da = method.getAnnotation(DynamicDataSourceAnnotation.class);
				String dataSource = da.dataSource();
				alog.info("========== remove dataSource "+dataSource+" ==========");
				DynamicDatasourceHolder.removeDataSourceKey();
			}
		}
	}
}

前置增强:利用反射,获取注解值,保存到threadlocal里。执行逻辑:获取目标类,如果目标类上标了自定义注解则继续执行,获取目标方法名称,获取参数类型列表,最终获取到方法,获取其上的注解值,设置到上下文数据源key维护类的threadlocal里。

后置增强:清空threadlocal。

核心代码已经完成了,接下来是测试代码。

mapper包:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间应该是对应接口的包名+接口名 -->
<mapper namespace="cn.tnt.aop.mapper.MsgDao">
    <!-- 增加 -->
    <insert id="save">
        insert into msg_tnt(ID,NAME)
        values(#{id},#{name})
    </insert>
    
    <select id="query" resultType="java.util.Map">
    	select * from msg_tnt
    </select>
</mapper>
package cn.tnt.aop.mapper;

import java.util.List;

import cn.tnt.aop.entity.Msg;

public interface MsgDao {
	int save(Msg msg);
	List query();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间应该是对应接口的包名+接口名 -->
<mapper namespace="cn.tnt.aop.mapper.GoodsDao">
    <!-- 增加 -->
    <insert id="save">
        insert into uc_tnt_goods(GOODS_ID,GOODS_NAME)
        values(#{goodsId},#{goodsName})
    </insert>
    <select id="query" resultType="java.util.Map">
    	select * from uc_tnt_goods
    </select>
</mapper>
package cn.tnt.aop.mapper;

import java.util.List;

import cn.tnt.aop.entity.Goods;

public interface GoodsDao {
	int save(Goods goods);
	List query();
}

service包

package cn.tnt.aop.service;

import cn.tnt.aop.entity.Msg;

public interface MsgService {
	String save(Msg msg);
	String query();
}
package cn.tnt.aop.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.tnt.aop.annotation.DynamicDataSourceAnnotation;
import cn.tnt.aop.entity.Msg;
import cn.tnt.aop.mapper.MsgDao;
@DynamicDataSourceAnnotation
@Service
public class MsgServiceImpl implements MsgService{
	private static final Logger log=LoggerFactory.getLogger(MsgServiceImpl.class);
	@Autowired
	private MsgDao msgDao;
	
	@DynamicDataSourceAnnotation(dataSource="msgDatasource")
	@Transactional
	@Override
	public String save(Msg msg) {
		int save = msgDao.save(msg);
		log.info("save msg "+msg+",save="+save);
		System.out.println(1/0);
		return "{'msg':'ok'+,'res':"+save+"}";
	}
	
	@DynamicDataSourceAnnotation(dataSource="msgDatasource")
	@Override
	public String query() {
		return msgDao.query().toString();
	}

}
package cn.tnt.aop.service;

import cn.tnt.aop.entity.Goods;

public interface GoodsService {
//	String query(String id);
	String query();
	String save(Goods goods);
}
package cn.tnt.aop.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.tnt.aop.annotation.DynamicDataSourceAnnotation;
import cn.tnt.aop.entity.Goods;
import cn.tnt.aop.mapper.GoodsDao;

@DynamicDataSourceAnnotation
@Service
public class GoodsServiceImpl implements GoodsService{
	private static final Logger log=LoggerFactory.getLogger(GoodsServiceImpl.class);
	@Autowired
	private GoodsDao goodsDao;
	
//	@DynamicDataSourceAnnotation(dataSource="goodsDatasource")
//	@Override
//	public String query(String id) {
//		log.info("##########"+id);
		throw new RuntimeException("test error");
//		return "{'goodsName':apple,'id':"+id+"}";
//	}
	
	@DynamicDataSourceAnnotation(dataSource="goodsDatasource")
	@Transactional
	@Override
	public String save(Goods goods) {
		int save = goodsDao.save(goods);
		log.info("save goods "+goods+",save="+save);
		return "{'msg':'ok'+,'res':"+save+"}";
	}
	
	@DynamicDataSourceAnnotation(dataSource="goodsDatasource")
	@Override
	public String query() {
		return goodsDao.query().toString();
	}

}

controller

package cn.tnt.aop.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.tnt.aop.entity.Goods;
import cn.tnt.aop.entity.Msg;
import cn.tnt.aop.service.GoodsService;
import cn.tnt.aop.service.MsgService;

@RestController
@RequestMapping("/test")
public class TestController {
	@Autowired
	private GoodsService goodsService;
	@Autowired
	private MsgService msgService;
	
	@RequestMapping("/q")
	public String query(){
		Map map=new HashMap();
		map.put("goods", goodsService.query());
		map.put("msg", msgService.query());
		return map.toString();
	}
	
	@RequestMapping("/s")
	public String save(){
		Goods g=new Goods();
		g.setGoodsId(12);
		g.setGoodsName("aaa");
		String gs = goodsService.save(g);
		
		Msg msg=new Msg();
		msg.setId(12);
		msg.setName("aaa");
		String ms = msgService.save(msg);
		
		return gs+"---"+ms;
	}
}

查询测试:

2018-11-06 15:14:38 DEBUG [org.springframework.web.servlet.DispatcherServlet] DispatcherServlet with name 'springServlet' processing POST request for [/tnt-service/test/q]
2018-11-06 15:14:38 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Looking up handler method for path /test/q
2018-11-06 15:14:38 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Returning handler method [public java.lang.String cn.tnt.aop.controller.TestController.query()]
2018-11-06 15:14:38 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'testController'
2018-11-06 15:14:38 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'dataSourceAspect'
2018-11-06 15:14:38 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== cn.tnt.aop.service.GoodsServiceImpl.query ==========
2018-11-06 15:14:38 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== 	set dataSource goodsDatasource ==========
2018-11-06 15:14:38 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Fetching JDBC Connection from DataSource
2018-11-06 15:14:38 INFO [cn.tnt.aop.datasource.DynamicDataSource] 数据源对应的key设置为:goodsDatasource
Tue Nov 06 15:14:38 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2018-11-06 15:14:39 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Returning JDBC Connection to DataSource
2018-11-06 15:14:39 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== cn.tnt.aop.service.GoodsServiceImpl.query ==========
2018-11-06 15:14:39 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== remove dataSource goodsDatasource ==========
2018-11-06 15:14:39 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== cn.tnt.aop.service.MsgServiceImpl.query ==========
2018-11-06 15:14:39 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== 	set dataSource msgDatasource ==========
2018-11-06 15:14:39 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Fetching JDBC Connection from DataSource
2018-11-06 15:14:39 INFO [cn.tnt.aop.datasource.DynamicDataSource] 数据源对应的key设置为:msgDatasource
Tue Nov 06 15:14:42 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2018-11-06 15:14:42 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Returning JDBC Connection to DataSource
2018-11-06 15:14:42 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== cn.tnt.aop.service.MsgServiceImpl.query ==========
2018-11-06 15:14:42 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== remove dataSource msgDatasource ==========
2018-11-06 15:14:42 DEBUG [org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdviceChain] Invoking ResponseBodyAdvice chain for body={msg=[{ID=1, NAME=msg}, {ID=11, NAME=aaa}], goods=[{GOODS_NAME=goods, GOODS_ID=1}, {GOODS_NAME=aaa, GOODS_ID=11}, {GOODS_NAME=aaa, GOODS_ID=12}]}
2018-11-06 15:14:42 DEBUG [org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdviceChain] After ResponseBodyAdvice chain body={msg=[{ID=1, NAME=msg}, {ID=11, NAME=aaa}], goods=[{GOODS_NAME=goods, GOODS_ID=1}, {GOODS_NAME=aaa, GOODS_ID=11}, {GOODS_NAME=aaa, GOODS_ID=12}]}
2018-11-06 15:14:42 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor] Written [{msg=[{ID=1, NAME=msg}, {ID=11, NAME=aaa}], goods=[{GOODS_NAME=goods, GOODS_ID=1}, {GOODS_NAME=aaa, GOODS_ID=11}, {GOODS_NAME=aaa, GOODS_ID=12}]}] as "text/plain;charset=ISO-8859-1" using [org.springframework.http.converter.StringHttpMessageConverter@5c0c6cba]
2018-11-06 15:14:42 DEBUG [org.springframework.web.servlet.DispatcherServlet] Null ModelAndView returned to DispatcherServlet with name 'springServlet': assuming HandlerAdapter completed request handling
2018-11-06 15:14:42 DEBUG [org.springframework.web.servlet.DispatcherServlet] Successfully completed request
2018-11-06 15:14:42 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'sqlSessionFactory'

先执行数据源切换aop的前置增强,设置数据源set dataSource goodsDatasource,把goodsDatasource保存到threadlocal。获取连接,先获取数据源key goodsDatasource,获取到其对应的数据源。后置增强 移除数据源 key goodsDatasource。然后前置增强 设置msgDatasource 移除 msgDatasource。

2个数据源都已经生效。

再测试下保存,顺便验证下事物:

在MsgServiceImpl先注掉 System.out.println(1/0);

2018-11-06 15:24:29 DEBUG [org.springframework.web.servlet.DispatcherServlet] DispatcherServlet with name 'springServlet' processing POST request for [/tnt-service/test/s]
2018-11-06 15:24:29 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Looking up handler method for path /test/s
2018-11-06 15:24:29 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Returning handler method [public java.lang.String cn.tnt.aop.controller.TestController.save()]
2018-11-06 15:24:29 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'testController'
2018-11-06 15:24:29 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'dataSourceAspect'
2018-11-06 15:24:29 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== cn.tnt.aop.service.GoodsServiceImpl.save ==========
2018-11-06 15:24:29 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== 	set dataSource goodsDatasource ==========
2018-11-06 15:24:29 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'transactionManager'
2018-11-06 15:24:29 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Creating new transaction with name [cn.tnt.aop.service.GoodsServiceImpl.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2018-11-06 15:24:29 INFO [cn.tnt.aop.datasource.DynamicDataSource] 数据源对应的key设置为:goodsDatasource
Tue Nov 06 15:24:29 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2018-11-06 15:24:29 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Acquired Connection [com.mysql.jdbc.JDBC4Connection@770d8d36] for JDBC transaction
2018-11-06 15:24:29 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@770d8d36] to manual commit
2018-11-06 15:24:29 INFO [cn.tnt.aop.service.GoodsServiceImpl] save goods Goods [goodsId=12, goodsName=aaa],save=1
2018-11-06 15:24:29 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Initiating transaction commit
2018-11-06 15:24:29 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@770d8d36]
2018-11-06 15:24:29 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@770d8d36] after transaction
2018-11-06 15:24:29 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Returning JDBC Connection to DataSource
2018-11-06 15:24:29 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== cn.tnt.aop.service.GoodsServiceImpl.save ==========
2018-11-06 15:24:29 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== remove dataSource goodsDatasource ==========
2018-11-06 15:24:29 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== cn.tnt.aop.service.MsgServiceImpl.save ==========
2018-11-06 15:24:29 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== 	set dataSource msgDatasource ==========
2018-11-06 15:24:29 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Creating new transaction with name [cn.tnt.aop.service.MsgServiceImpl.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2018-11-06 15:24:29 INFO [cn.tnt.aop.datasource.DynamicDataSource] 数据源对应的key设置为:msgDatasource
Tue Nov 06 15:24:29 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2018-11-06 15:24:30 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Acquired Connection [com.mysql.jdbc.JDBC4Connection@11f67679] for JDBC transaction
2018-11-06 15:24:30 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@11f67679] to manual commit
2018-11-06 15:24:30 INFO [cn.tnt.aop.service.MsgServiceImpl] save msg Msg [id=12, name=aaa],save=1
2018-11-06 15:24:30 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Initiating transaction commit
2018-11-06 15:24:30 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@11f67679]
2018-11-06 15:24:30 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@11f67679] after transaction
2018-11-06 15:24:30 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Returning JDBC Connection to DataSource
2018-11-06 15:24:30 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== cn.tnt.aop.service.MsgServiceImpl.save ==========
2018-11-06 15:24:30 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== remove dataSource msgDatasource ==========
2018-11-06 15:24:30 DEBUG [org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdviceChain] Invoking ResponseBodyAdvice chain for body={'msg':'ok'+,'res':1}---{'msg':'ok'+,'res':1}
2018-11-06 15:24:30 DEBUG [org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdviceChain] After ResponseBodyAdvice chain body={'msg':'ok'+,'res':1}---{'msg':'ok'+,'res':1}
2018-11-06 15:24:30 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor] Written [{'msg':'ok'+,'res':1}---{'msg':'ok'+,'res':1}] as "text/plain;charset=ISO-8859-1" using [org.springframework.http.converter.StringHttpMessageConverter@6e35c6b5]

日志可以看出 2个数据源都生效,可以看出每个数据源的事物都单独开启提交,事物生效。

把在MsgServiceImpl先注掉的 System.out.println(1/0)放开;

2018-11-06 15:28:22 DEBUG [org.springframework.web.servlet.DispatcherServlet] DispatcherServlet with name 'springServlet' processing POST request for [/tnt-service/test/s]
2018-11-06 15:28:22 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Looking up handler method for path /test/s
2018-11-06 15:28:22 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Returning handler method [public java.lang.String cn.tnt.aop.controller.TestController.save()]
2018-11-06 15:28:22 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'testController'
2018-11-06 15:28:22 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'dataSourceAspect'
2018-11-06 15:28:22 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== cn.tnt.aop.service.GoodsServiceImpl.save ==========
2018-11-06 15:28:22 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== 	set dataSource goodsDatasource ==========
2018-11-06 15:28:22 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'transactionManager'
2018-11-06 15:28:22 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Creating new transaction with name [cn.tnt.aop.service.GoodsServiceImpl.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2018-11-06 15:28:22 INFO [cn.tnt.aop.datasource.DynamicDataSource] 数据源对应的key设置为:goodsDatasource
Tue Nov 06 15:28:23 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Acquired Connection [com.mysql.jdbc.JDBC4Connection@26713fc8] for JDBC transaction
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@26713fc8] to manual commit
2018-11-06 15:28:23 INFO [cn.tnt.aop.service.GoodsServiceImpl] save goods Goods [goodsId=122, goodsName=aaa],save=1
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Initiating transaction commit
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@26713fc8]
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@26713fc8] after transaction
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Returning JDBC Connection to DataSource
2018-11-06 15:28:23 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== cn.tnt.aop.service.GoodsServiceImpl.save ==========
2018-11-06 15:28:23 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== remove dataSource goodsDatasource ==========
2018-11-06 15:28:23 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== cn.tnt.aop.service.MsgServiceImpl.save ==========
2018-11-06 15:28:23 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceBefore] ========== 	set dataSource msgDatasource ==========
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Creating new transaction with name [cn.tnt.aop.service.MsgServiceImpl.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2018-11-06 15:28:23 INFO [cn.tnt.aop.datasource.DynamicDataSource] 数据源对应的key设置为:msgDatasource
Tue Nov 06 15:28:23 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Acquired Connection [com.mysql.jdbc.JDBC4Connection@764ac3c2] for JDBC transaction
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@764ac3c2] to manual commit
2018-11-06 15:28:23 INFO [cn.tnt.aop.service.MsgServiceImpl] save msg Msg [id=122, name=aaa],save=1
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Initiating transaction rollback
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@764ac3c2]
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@764ac3c2] after transaction
2018-11-06 15:28:23 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Returning JDBC Connection to DataSource
2018-11-06 15:28:23 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== cn.tnt.aop.service.MsgServiceImpl.save ==========
2018-11-06 15:28:23 INFO [cn.tnt.aop.datasource.DataSourceAspect.sourceAfter] ========== remove dataSource msgDatasource ==========
2018-11-06 15:28:23 DEBUG [org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver] Resolving exception from handler [public java.lang.String cn.tnt.aop.controller.TestController.save()]: java.lang.ArithmeticException: / by zero
2018-11-06 15:28:23 DEBUG [org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver] Resolving exception from handler [public java.lang.String cn.tnt.aop.controller.TestController.save()]: java.lang.ArithmeticException: / by zero
2018-11-06 15:28:23 DEBUG [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] Resolving exception from handler [public java.lang.String cn.tnt.aop.controller.TestController.save()]: java.lang.ArithmeticException: / by zero
2018-11-06 15:28:23 DEBUG [org.springframework.web.servlet.DispatcherServlet] Could not complete request
java.lang.ArithmeticException: / by zero
	at cn.tnt.aop.service.MsgServiceImpl.save(MsgServiceImpl.java:25)
	at cn.tnt.aop.service.MsgServiceImpl$$FastClassBySpringCGLIB$$70bec930.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:267)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
	at cn.tnt.aop.service.MsgServiceImpl$$EnhancerBySpringCGLIB$$54a2b9fa.save(<generated>)
	at cn.tnt.aop.controller.TestController.save(TestController.java:41)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:498)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:1025)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1115)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)
2018-11-06 15:28:23 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'sqlSessionFactory'
十一月 06, 2018 3:28:23 下午 org.apache.catalina.core.StandardWrapperValve invoke
严重: Servlet.service() for servlet [springServlet] in context with path [/tnt-service] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause
java.lang.ArithmeticException: / by zero
	at cn.tnt.aop.service.MsgServiceImpl.save(MsgServiceImpl.java:25)
	at cn.tnt.aop.service.MsgServiceImpl$$FastClassBySpringCGLIB$$70bec930.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:267)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
	at cn.tnt.aop.service.MsgServiceImpl$$EnhancerBySpringCGLIB$$54a2b9fa.save(<generated>)
	at cn.tnt.aop.controller.TestController.save(TestController.java:41)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:498)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:1025)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1115)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)

可见goodsDatasource的事物已经提交,msgDatasource的事物回滚,可见事物是生效的。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值