Spring同一个类中方法调用注解失效

在项目开发中进行事务管理的时候,遇到给类的方法加了@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。

3.通过Spring应用上下文获得bean后进行调用(ApplicationContext.getBean())

对于SpringBoot的应用可以这样获得应用上下文。

Spring应用上下文工具类,这里只写了两种getBean()。

public class SpringContextUtil {
 
  private static ApplicationContext applicationContext;
 
  public static ApplicationContext getApplicationContext() {
    return applicationContext;
  }
 
  public static void setApplicationContext(ApplicationContext applicationContext) {
    SpringContextUtil.applicationContext = applicationContext;
  }
 
  @SuppressWarnings("unchecked")
  public static <T> T getBean(String name) {
    return (T) applicationContext.getBean(name);
  }
 
  public static <T> T getBean(Class<T> clazz) {
    return applicationContext.getBean(clazz);
  }
}

SpringBoot启动时设置applicationContext的值。

@SpringBootApplication
@ServletComponentScan
public class SpringBootApplication {
 
  public static void main(String[] args) {
    ApplicationContext context = SpringApplication.run(SpringBootApplication.class, args);
    SpringContextUtil.setApplicationContext(context);
  }
}

可以在test1中通过SpringContextUtil.getBean(“TestService”).test2()进行调用。


@Service
public class TestService {
 
    public void test1(){
        SpringContextUtil.getBean("TestService").test2();
    }
     
    @Transactional
    public void test2(){
    }
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当在同一个类中方法相互调用时,如果希望事务能够生效,可以采取以下解决办法: 1. 使用代理调用方法:由于Spring事务管理是通过AOP代理实现的,所以可以通过使用代理对象调用方法来触发事务管理。可以通过将方法调用委托给代理对象来确保事务的生效。 2. 将被调用方法抽取到另一个类中:将被调用方法抽取到另一个类中,并确保在被调用方法上添加@Transactional注解。这样,在调用方法调用被抽取的方法时,事务将能够生效。 3. 使用AspectJ模式的事务管理:Spring还提供了AspectJ模式的事务管理,可以在同一个类中方法相互调用时保持事务的生效。通过配置AspectJ的切面来实现事务的管理,可以细粒度地控制事务的传播行为和回滚条件。 需要注意的是,以上解决办法需要根据具体情况选择合适的方式,并确保在调用方法上正确地添加@Transactional注解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Spring同一个service中方法相互调用事务不生效问题解决方案](https://blog.csdn.net/a1036645146/article/details/107469578)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值