zoukankan      html  css  js  c++  java
  • Spring同一个类中方法调用导致@Transactional失效——解决方案!

    在项目开发中进行事务管理的时候,遇到给类的方法加了@Transactional,同一个类进行调用不生效的问题。

    原因分析

    通常在使用Spring Aop注解的时候,如@Transactional, @Cacheable等注解一般需要在类方法第一个入口的地方加才会生效。这是由于这些注解基于Spring AOP代理实现的,所以不支持内部调用的。
    举个简单例子:

    @RestController
    public class TestController {
        @Autowired
        private TestService testService;
         
        public void test(){
             
        }
    }
    
    
    @Service
    public class TestService {
     
        public void test1(){
            test2();
        }
         
        @Transactional
        public void test2(){
        }
    }
    

    TestController直接调用TestService 的test2()方法,test2()上的@Transactional生效,如果是调用test1()方法,test2()上的@Transactional不生效。因为在test1方法中调用test2方法相当于使用this.test2(),this代表的是Service类本身,并不是真实的代理Service对象,这种不能实现代理功能。Spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,Spring 会为这个bean动态地生成一个子类(即代理类proxy),proxy是继承原来那个bean的。此时,当这个有注解的方法被调用的时候,实际上是由proxy来调用的,proxy在调用之前就会启动transaction。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过proxy,而是直接通过原来的那个bean,所以就不会启动transaction,即@Transactional注解无效。

    解决办法

    1.把两个方法写到不同的类中去

    直接定义在另外类中即可,这里不再演示

    2.AopContxt.currentProxy()获得当前代理对象

    AopContext源码

    public final class AopContext {
     
        private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
     
        private AopContext() {
        }
     
        public static Object currentProxy() throws IllegalStateException {
            Object proxy = currentProxy.get();
            if (proxy == null) {
                throw new IllegalStateException(
                        "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
            }
            return proxy;
        }
     
        @Nullable
        static Object setCurrentProxy(@Nullable Object proxy) {
            Object old = currentProxy.get();
            if (proxy != null) {
                currentProxy.set(proxy);
            }
            else {
                currentProxy.remove();
            }
            return old;
        }
    }
    

    Spring会将当前代理对象绑定到当前线程ThreadLocal中,可以在test1中通过((TestService) AopContext.currentProxy()).test2()进行调用。

    
    @Service
    public class TestService {
     
        public void test1(){
            ((TestService) AopContext.currentProxy()).test2();
        }
         
        @Transactional
        public void test2(){
        }
    }
    

    在使用AopContxt.currentProxy()时需要“exposeProxy”设置为“true”,可以在类上添加【@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)】注解。但是,对于expose-proxy=true的使用,因为将代理对象放到ThreadLocal中,有性能损耗,官方默认是false。

    艾欧尼亚,昂扬不灭,为了更美好的明天而战(#^.^#)
  • 相关阅读:
    [iOS]Xcode+GitHub远程代码托管(GIT, SVN)
    [iOS]Xcode处理过时方法的警告
    [iOS]@synthesize和@dynamic关键字
    [iOS]图片高清度太高, 导致内存过大Crash
    [软件]Xcode查找系统framework所在路径
    [软件]在浏览器里添加MarkDown Here(插件)
    [PHP]利用XAMPP搭建本地服务器, 然后利用iOS客户端上传数据到本地服务器中(四. iOS端代码实现)
    ios -Unity3D的EasyAR集成到已经有项目中。
    iOS创建安全的单例
    阿里云轻量应用服务器 配置mysql详解(转载)
  • 原文地址:https://www.cnblogs.com/lovelywcc/p/14418130.html
Copyright © 2011-2022 走看看