当用AopContext.currentProxy()方式调用@Asyn注解的方法,发现不起作用了

1、前言

遇到到平常一些spring相关调用错误

2、我们常常会用到在一个类的方法内部会去调用本类的别一个方法

我们常常会用到在一个类的方法内部会去调用本类的别一个方法,示例如下:

public interface ITestAsy {
    String funttion1();
    String function2();
}

@Component
public class TestAsy implements ITestAsy {
 @Override
    public String funttion1() {
        this.function2();
        return "1";
    }

    @Override
    public String function2() {
        return "2";
    }
}

上面都是普通方法,调用完全没问题,
但后来function2可能执行时间很长,funttion1在调用funttion2时不用等待funttion2的返回,那么我们是不是想到用另一个线程去调用function2。
而spring给我们提供了注解@Async,可以很容易实现

2.2 用@Async实现对方法的异步调用

我们知道@Async是会给这个类生成一个代理的,实在代理类中会会将这个方法打包成一个callAble 提交给线程池,这样spring就帮我们实现了只需通过注解的方式 就可以异步调用的目的。
如果我们这样调:

 @Test
public void ttttt(){
        System.out.println(Thread.currentThread().getName());
        iTestAsy.function2();
    }

打印线程如下:

main
SimpleAsyncTaskExecutor-1

说明调用iTestAsy.function2();另起了一个线程调用。

如果我们需在funtion1中去调用funciton2呢,这样写行不行?

 @Override
public String funttion1() {
        System.out.println(Thread.currentThread().getName());
        this.function2();
        return "1";
}

可以看下测试效果:

@Autowired
private ITestAsy iTestAsy;

@Test
public void ttttt(){
    System.out.println(Thread.currentThread().getName());
    iTestAsy.funttion1();
}

打印如下:

main
main
main

说明this.function2(); 这里this 不是代理对象自然不行。
换种写法,通过spring上下文来获取这个对象实例 然后再调用funciton2,如下:

@Override
public String funttion1() {
    System.out.println(Thread.currentThread().getName());
    ITestAsy bean = applicationContext.getBean(ITestAsy.class);
    bean.function2();
    return "1";
}

打印如下:

main
main
SimpleAsyncTaskExecutor-1

funtion2的调用在另一个想成中。@Aync注解起作用了。
而除了通过applicationContext获取对象去调这个方法外,还有一种方式,就是通过获取绑定在本线程上的代理对象 来调用这个方法

2.3、用AopContext.currentProxy();获取本实例的代理对象 来调用带@Aync注解的方法

代理实例如下:

  @Override
 public String funttion1() {
      System.out.println(Thread.currentThread().getName());
      ITestAsy bean = (ITestAsy) AopContext.currentProxy();
      bean.function2();
      return "1";
  }

调用发现报错如下:
在这里插入图片描述
让我们开启exposeProxy = true,于是我们在类上另上注解:

@EnableAsync
@EnableAspectJAutoProxy(exposeProxy =  true)
@Configuration
public class TestConfig {


}

再继续调用,还是报错:
java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
如果在function1上加上@Transactional注解,调用成功了
如下:

@Override
@Transactional
public String funttion1() {
    System.out.println(Thread.currentThread().getName());
    ITestAsy bean = (ITestAsy) AopContext.currentProxy();
    bean.function2();
    return "1";
}
@Async
@Override
public String function2() {
    System.out.println(Thread.currentThread().getName());
    return "2";
}

main
main
SimpleAsyncTaskExecutor-1

2.4、为什么用用AopContext.currentProxy();调用@Asyn注解的方法 还是报错,而在这个方法上加上@Transactional注解就不报错了

为什么用用AopContext.currentProxy();调用@Asyn注解的方法 还是报错,而在这个方法上加上@Transactional注解就不报错了,那么需要看下@Asyn 生成的代理和@Transactional代理有什么不一样。
一定是@Transactional 设置了exposeProxy = true,所以不报错了。

2.5、看下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;
	}

}

发现只有currentProxy.get() 获取到值后,才不会报出异常,那么也就是说必须调用setCurrentProxy这个方法。
我们是通过@EnableAspectJAutoProxy(exposeProxy = true)这个注解来使得支持AopContext.currentProxy()调用方式,那么我们先看下这个注解的作用

2.6、@EnableAspectJAutoProxy(exposeProxy = true)注解的作用

@EnableAspectJAutoProxy(exposeProxy = true) 它导入了@Import(AspectJAutoProxyRegistrar.class)

public abstract class AopConfigUtils {
	public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
		}
	}
	public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
		}
	}
}

这个注解标注的属性最终都被设置到了internalAutoProxyCreator 这个bean上,将exposeProxy这个属性设置成了true

2.7、internalAutoProxyCreator

internalAutoProxyCreator 这个bean是spring内部创建自动代理创建器。
这个自动代理创建器,又是什么类呢? 我们回顾下 如果开启@Transactional注解。是不是通过@EnableTransactionManagement 这个注解开启事务注解的,那么internalAutoProxyCreator这个bean肯定会通过@EnableTransactionManagement这个注解注入。
看下TransactionManagementConfigurationSelector

 protected String[] selectImports(AdviceMode adviceMode) {
    switch(adviceMode) {
    //默认是这个模式,那么会导入AutoProxyRegistrar这个bean
    case PROXY:
        return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
    case ASPECTJ:
        return new String[]{this.determineTransactionAspectClass()};
    default:
        return null;
    }
}

AutoProxyRegistrar#registerBeanDefinitions在这里插入图片描述
在这里插入图片描述
最后注入是这个InfrastructureAdvisorAutoProxyCreator
这个类也是一个BeanPostProcessor, 也会调用postProcessBeforeInstantiation这个方法去创建一个代理类,最终会生成cblib或jdk动态代理类:
在这里插入图片描述

2.71、JdkDynamicAopProxy#invoke()

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    @Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	    //如果为true,那么就会把currentProxy设置当前代理对象,所以通过get方法可以得到proxy对象,所以我们可以设置@EnableAspectJAutoProxy(exposeProxy =  true)注解
	    if (this.advised.exposeProxy) {
			// Make invocation available if necessary.
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}
	}
}

同理cglib代理中也是如此:
在这里插入图片描述

2.8、看看@Async是如何生成代理的

看看@Async是如何生成代理的,它是通过@EnableAsync注解生效的。
这个注解会导入ProxyAsyncConfiguration,而这个会创建AsyncAnnotationBeanPostProcessor。所以@Async这个注解的代理对象是通过AsyncAnnotationBeanPostProcessor创建的,而@EnableAspectJAutoProxy(exposeProxy = true)这个注解只会给internalAutoProxyCreator设置属性exposeProxy=true。所以@Async注解的方法 通过AopContext.currentProxy()得不到代理对象。
那么要想获取到代理里面,通过application.getBean()得到,或者我们也学@EnableAspectJAutoProxy(exposeProxy = true)这个注解一样,给AsyncAnnotationBeanPostProcessor 设置exposeProxy=true

2.9、给AsyncAnnotationBeanPostProcessor也设置exposeProxy=true

@Component
public class AsyncForceToExposeProxyBeanPostProcess implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

        BeanDefinition definition = configurableListableBeanFactory.getBeanDefinition(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);
        definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
    }
}

这时不加@Transactional再调用function1:

@Override
public String funttion1() {
    System.out.println(Thread.currentThread().getName());
    ITestAsy bean = (ITestAsy) AopContext.currentProxy();
    bean.function2();
    return "1";
}
@Async
@Override
public String function2() {
    System.out.println(Thread.currentThread().getName());
    return "2";
}

 @Test
 public void ttttt(){
     System.out.println(Thread.currentThread().getName());
     iTestAsy.funttion1();
 }

结果如下:

main
main
SimpleAsyncTaskExecutor-1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值