Spring中annotation-driven配置事务管理器详解
2015-07-01 16:37
841 查看
@Transactional
假定spring 容器中定义了两个事务管理器:transactionManagerX、transactionManagerY,它们分管两个数据源:datasourceX、datasourceY。<tx:annotation-driven transaction-manager="transactionManagerX" /> <tx:annotation-driven transaction-manager="transactionManagerY" />
(spring容器中的定义顺序如上)
有如下应用代码:
public interface TestEntityService { public void methodX(); public void methodY(); }
接口实现类1:
public class TestEntityServiceImpl implements TestEntityService { @Resource private TestEntityDao testEntityDao;//实际操作的是datasourceX @Transactional public void methodX() { testEntityDao.xxx(); testEntityDao.zzz(); } public void methodY() { } }
接口实现类2:
public class AnotherTestEntityServiceImpl implements TestEntityService { @Resource private TestEntityDao anOtherTestEntityDao;//实际操作的是datasourceY @Transactional public void methodX() { testEntityDao.mmm(); testEntityDao.nnn(); } public void methodY() { } }
假设方法methodX()是需要事务控制的,通常我们是直接在方法上添加@Transactional标注,但是好像spring3.0(具体版本没弄清)之前的Transactional标注不支持区分使用哪个事务管理器。3.0之后的版本Transactional增加了个string类型的value属性来特殊指定加以区分。
例如:
@Transactional("aaaaa"),即显示的要求spring用id="aaaaa"的事务管理器来管理事务。该属性亦可省略(省略的话用容器中缺省的事务管理器)
对于该属性的用法做了如下测试:
methodX() 事务生效测试结果 | @Transactional ("transactionManagerX") | @Transactional ("transactionManagerY") | @Transactional ("transactionManagerZ") transactionManagerZ为未定义过的 | @Transactional |
TestEntityServiceImpl (实际使用datasourceX) | Y | N | Y | Y |
AnotherTestEntityServiceImpl (实际使用datasourceY) | N | Y | N | N |
<tx:annotation-driven transaction-manager="transactionManagerY" /> <tx:annotation-driven transaction-manager="transactionManagerX" />以下为测试结果:
methodX() 事务生效测试结果 | @Transactional ("transactionManagerX") | @Transactional ("transactionManagerY") | @Transactional ("transactionManagerZ") transactionManagerZ为未定义过的 | @Transactional |
TestEntityServiceImpl (实际使用datasourceX) | Y | N | N | N |
AnotherTestEntityServiceImpl (实际使用datasourceY) | N | Y | Y | Y |
1、当在@Transactional("xxx")中正确指定了需要使用的事务管理器时,事务控制正常;
2、如果@Transactional指定了未定义过的事务管理器,spring将以缺省事务管理器来处理。(如果程序正好使用的是缺省事务管理器同一个数据源,事务控制将生效);
3、如果@Transactional不指定事务管理器,使用缺省事务管理器;
4、如果@Transactional指定了不匹配的事务管理器(实际用到的数据源和指定的事务管理器控制的数据源不一致),事务控制将失效;
注:spring容器缺省事务管理器,是以加载顺序来判断,首先加载的作为缺省。
例如:
<tx:annotation-driven transaction-manager="transactionManagerX" /> <tx:annotation-driven transaction-manager="transactionManagerY" />定义在同一个文件中,则第一个transactionManagerX作为缺省。
定义在不同文件,则按文件的加载顺序,首先加载的作为缺省。
建议:实际代码中需要用到@Transactional时,即使默认只有一个transactionManager,@Transactional也将其标明。以提高新增数据源后代码可读性,另外防止将来定义多个数据源后,以前的缺省事务管理器不被spring默认为缺省了(比如哪天上线新定义了一个数据源,刚好新定义的transactionManager被先加载了,就会导致代码问题。)
bean的配置使用
容器中加了<tx:annotation-driven >(需要增加一些xsd)之后,需要事务控制的的service,不需要再具体的bean上做其他的配置,例如用代理包装。直接配置即可:<bean id="testEntityService" class="com.xxx.impl.TestEntityServiceImpl"/>
spring将由JdkDynamicAopProxy 生成代理过的类提供使用。
这种用法的效果和下面配置使用效果一样。都是由JdkDynamicAopProxy 生成代理对象提供使用。
我觉得区别是下面的方法在事务控制的代码可读性上不好,因为哪个方法需要事务控制和控制粒度都在配置文件中,和代码分开了。
<bean id="testEntityService3" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="transactionManagerX" /> <property name="target"> <bean class="com.xxxx.impl.TestEntityServiceImpl" /> </property> <property name="proxyInterfaces" value="com.xxxx.TestEntityService"/> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
方法的可见度和 @Transactional
@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅@Transactional注解的出现不足于开启事务行为,它仅仅是一种元数据,能够被识别。
@Transactional 注解和上述的配置适当的具有事务行为的beans所使用。
上面的例子中,其实正是 <tx:annotation-driven/>元素的出现开启了事务行为。
Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的。
实际开发中,多半喜欢将持久化操作的代码集中抽出为另一个方法(因为不想事务被无关的业务代码托的持续太长),然后在抽取出来的方法上加上@Transactional,这样的结果是被抽离出的代码即使加了事务标记,也根本起不到事务控制的效果(不管是private和public)。
例如:
public class TestEntityServiceImpl implements TestEntityService { @Resource private TestEntityDao testEntityDao;//实际操作的是datasourceX @Transactional public void methodX() { testEntityDao.xxx(); testEntityDao.zzz(); } public void methodY() { methodX() } }如果执行TestEntityService.methodY();事务是不生效的。只有TestEntityService.methodX();才生效。
从spring实现这些的原理(动态代理和aop)上来看,spring只拦截外部调用,方法的内部调用通常是不被aop支持的。
详细原理可以参考:/article/8299859.html
相关文章推荐
- 关于 ndk和jni的区别
- Spring boot 文件上传
- java 内存划分与作用
- SpringMVC 返回json数据的三种方式
- eclipse svn快捷键
- Java中的private、protected、public和default的区别
- SpringMVC集中传参方式
- myeclipse断点失效
- Maven的SSH搭建以及部署
- 在SpringMVC中获取request对象的几种方式
- jdk1.8 ossbrowser打不开
- Java语言基础(二)——数据类型、运算符和流程控制语句
- Java 递归、尾递归、非递归 处理阶乘问题
- java下载文件名中带有中文字符乱码的处理
- java简单抓取网页源代码
- 搭建springMVC+Mybatis+quartz+log4j+maven项目
- eclipse svn安装_eclipse svn插件下载地址及安装教程
- Java学习篇之---Mysql中varchar类型总结
- javascirpt 的闭包----我的简介
- window下在同一台机器上安装多个版本jdk,修改环境变量不生效问题处理办法