今日写代码发现了@Async注解的方法放在@Service的类中,不能异步执行。于是排查找了下原因。我是直接在类中定义了一个异步方法。然后直接在该类中的其他方法调用。
问题分析:
跟spring注册bean的过程有很大关系,首先调用这个方法如果不是静态类的,肯定是有个隐式对象去调用。在这个类中(命名为A,实例对象为a)。则应该是a.issueRecharge()方法调用,这里的a其实就是spring管理的一个bean。相当于A类在spring容器初始化加载在设置属性是要把a设置进去,自己依赖自己就是循环依赖。在依赖的过程中,像@Async,@Transation都是基于动态代理实现的。在实例化时第一次实例a,应该已经在3级缓存中已存在。但没产出最终的原始对象。在进行动态代理实例代理对象时,发现容器已经暴露了实例a的一个对象。那么这个代理对象就会创建不成功。最终拿到的a就是A的原始对象,所以@Async修饰的方法相当于一个普通方法,用的是a的原始对象。没有被spring代理去管控。
解决方案:
将这种异步方法封装成一个新的类。重新作为一个bean进行管理。
改造封装成B类后再进行依赖注入,此时应该是
A{
B b;
}
B{
A,a;
}
问题分析:
发现这时出现了循环依赖的报错。按理说是spring是通过三级缓存默认解决了这类问题。出现问题的原因还是关于代理对象的创建。非最终版本的classA被注入到了循环依赖的classB。所以当出现动态代理而又出现循环依赖的时候,要注意这种情况。通过懒加载方法实例化bean,在一个完成实例注册成功就会去拿到最终版本的bean。
解决方案:
在引入的bean加上@Lazy注解