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 {
}
再继续调用,还是报错:
如果在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上,这是什么bean