同一个类下,调用方法,采用AopContext方式,解决Spring注解失效

使用:AopContext.currentProxy() 这个解决Spring注解失效

原因简单来说: 同一个类内这样调用的话,只有第一次调用了动态代理生成的ProxyClass,之后一直用的是不带任何切面信息的

知道了原因,处理方法也特别简单只需要显示利用Spring暴露的AopContext即可

解决:

((YourClass) AopContext.currentProxy()).noTransactionTask(keyword);

例子:

		public void noTransactionTask(String keyword){    // 注意这里 调用了代理类的方法
            ((YourClass) AopContext.currentProxy()).transactionTask(keyword);
        }

        @Transactional
        void transactionTask(String keyword) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {        //logger
                //error tracking
            }
            System.out.println(keyword);
        }

spring boot注解记得要加上
@EnableAspectJAutoProxy(exposeProxy = true)来暴露AOP的Proxy对象才行,否则会报异常

Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.

注意:exposeProxy = true 是用来暴露AOP的Proxy对象的
产生代理对象就必须要产生一个切面,如果没有,创建代理对象的时候我们会发现对象中的exposeProxy属性还是false,不管你是否设置了exposeProxy = true。

ProxyCreatorSupport的createAopProxy方法:
在这里插入图片描述

像@Async这个只是简单的ioc,而没有aop ,创建代理对象的时候我们会发现对象中的exposeProxy属性总是false

而@Transactional 会将类定义成是一个切面。所以我们可以用 AopContext.currentProxy() 来解决同一个类下,调用方法事务的问题

例子:
@Async 想要用 AopContext.currentProxy() 最简单的方法就是在被调用方法上添加一个事务的注解 @Transactional :

		@Transactional
		@Async
        void transactionTask(String keyword) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {        //logger
                //error tracking
            }
            System.out.println(keyword);
        }

其实以上通过 AopContext.currentProxy() 这个方法去解决Spring注解失效的问题不是好的方案

更为简单的解决方案:

1.将被调用方法放到另一个类中

2.@Autowired注解让spring自动注入ApplicationContext,通过applicationContext.getBean(YourClass.class)获取类,然后调用你的方法

	/**
     *  注解让spring自动注入ApplicationContext
     */
    @Autowired
    private ApplicationContext applicationContext;

3.web项目可以通过WebApplicationContext 获取Spring容器上下文,通过wac.getBean(YourClass.class)获取类,然后调用你的方法

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
或者 (通过request或session加载spring容器)
WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext()); 
或者
ServletContext application = ServletActionContext.getServletContext();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application); 


4.写一个工具类获取Spring 的bean,然后通过 SpringContextUtil去获取类,然后调用你的方法

SpringContextUtil.getBean(YourClass.class)

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * Created by qinjp on 2018/3/28.
 */
@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Autowired
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz){
        return applicationContext.getBean(clazz);
    }
    
    public static <T> T getBean(String name, Class<T> requiredType) {
    	return applicationContext.getBean(name, requiredType);
    }
}

5.通过ApplicationListener获取spring 的上下文容器 ,获取类,然后调用你的方法

@Component // 注入spring容器
public class SpringManager implements ApplicationListener<ContextRefreshedEvent> {
    private static ApplicationContext applicationContext = null;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if(applicationContext == null){
            applicationContext = event.getApplicationContext();
        }
    }
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
    
	public static <T> T getBean(Class<T> clazz){
        return applicationContext.getBean(clazz);
    }
 
}

6.spring boot 通过入口main函数获取spring 的上下文容器 ,获取类,然后调用你的方法

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

import org.springframework.context.ApplicationContext;
 
public class SpringContextUtil {
    private static ApplicationContext applicationContext;
 
    public static ApplicationContext getApplicationContext(){
        return applicationContext;
    }
 
    public static void setApplicationContext(ApplicationContext applicationContext){
        SpringContextUtil.applicationContext = applicationContext;
    }
 
    public static <T> T getBean(String name){
        return (T)applicationContext.getBean(name);
    }
 
    public static <T> T getBean(Class<T> clazz){
        return applicationContext.getBean(clazz);
    }
}
  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当在同一个类中的方法相互调用时,如果希望事务能够生效,可以采取以下解决办法: 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 ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值