问题
在spring 中使用 @Transactional 、 @Cacheable 或 自定义 AOP 注解时,对象内部方法中调用该对象的其他使用aop机制的方法会失效。
@Transactional
public void saveFile(FileDetail sourceFile, FileDetail targetFile, FileRelation fileRelation) {
sourceFile = fileDao.queryFileByMd5(sourceFile.getMd5());
fileDao.insertFile(sourceFile);
fileRelationDao.insertFileRelation(fileRelation);
sendMessage();
}
public void sendMessage(){
System.out.println("打日志");
System.out.println("发送消息...");
}
在同一个类中的方法级别调用也会导致 aop 注解失效
原因
Spring AOP使用JDK动态代理和CGLib,由于没有接口的类,所以使用CGLib代理。当方法被代理时,其实通过动态代理生成了代理对象,然后代理对象执行invoke方法,在调用被代理对象的方法时,执行其他操作。问题就在于被代理对象的方法中调用被代理对象的其他方法时,使用的是被代理对象本身,而非代理对象。这就导致了一个方法时代理对象调用的,一个是被代理对象调用的。他们的调用始终不出于同一个对象。
实例
(1)当我们调用saveFile(),spring的动态代理会动态生成一个代理对象(serviceFile)。
(2)当我们调用saveFile的时候实际上是serviceFile调用。
(3)当调用saveFile()方法内调用同一个类的另外一个注解方法sendMessage时,实际上是使用this.saveFile(),而this指当前对象而非代理对象,所以注解失效。
解决方案
通过AopContext.currentProxy()获取当前代理对象。
1.AopContext.currentProxy();
2.修改 xml
@Transactional
public void saveFile(FileDetail sourceFile, FileDetail targetFile, FileRelation fileRelation) {
sourceFile = fileDao.queryFileByMd5(sourceFile.getMd5());
fileDao.insertFile(sourceFile);
fileRelationDao.insertFileRelation(fileRelation);
(FileService)AopContext.currentProxy().sendMessage();
}
@Transactional
public void sendMessage(){
System.out.println("打日志");
System.out.println("发送消息...");
}
配置
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceForSqlServer" />
</bean>
注意事项
1.在需要事务管理的地方加@Transactional 注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上 。
2.@Transactional 注解只能应用到 public 可见度的方法上 。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
3.注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据。必须在配置文件中使用配置元素,才真正开启了事务.
4.spring事物是基于类和接口的所以只能在类里面调用另一个类里面的事物,同一个类里面调用自己类的事物方法是无效的。spring事物也不要频繁使用,在事物处理的同时操作的第一张表会被限制查看的(即被临时锁住)。数据量大的时候会有一定影响。
参考
[1][spring aop注解失效之谜](http://blog.csdn.net/u012373815/article/details/77345655)
[2]Spring @Transactional事物配置无效原因
[3]Does Spring @Transactional attribute work on a private method?