Spring aop注解失效

时间:2023-03-08 18:50:43
Spring aop注解失效

问题

在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事物配置无效原因](http://blog.csdn.net/weitao233136/article/details/51841707)
[3][Does Spring @Transactional attribute work on a private method?](https://*.com/questions/4396284/does-spring-transactional-attribute-work-on-a-private-method)