背景
之前写了一个用自定义注解+切面进行日志和日常统一处理的方案,但是遇到了一个问题就是,如果注解标记的方法A 被同一个类中另一个方法B调用,当B方法被外部调用时,注解将不生效,切面无法拦截。即外部调用B方法时,注解@RpcCheck不会生效。
@RpcCheck
public void A() {
}
public void B() {
A();
}
当时查了一下解决原因,切面之所以会生效,是因为创建了代理对象,在调目标方法A之前,代理对象进行了增强处理(切面逻辑)。但是B调用A时,是类的内部方法调用,就不会再次创建代理对象,而是使用当前实例,所以也就不会进行增强处理了,即切面无法生效。
解决方案就是,在B调用A时,获取代理对象,使用代理对象调用。实现方式以下两种
//1. 使用AopContext.currentProxy()获取代理对象
public class UserService {
public void B() {
UserService userService = (UserService) AopContext.currentProxy();
userService.A();
}
}
//2. 自己注入自己, 即相当于外部直接调用A
public class UserService {
@Autowired
private UserService thisService;
public void B() {
thisService.A();
}
}
因为方法1需要强制类型转换,所以我果断选择了第二种,然后我就遇到了新问题。工程可以正常启动,但是我在写单测Mock方法的时候,会抛异常 circular reference (循环依赖),然后就进入了本篇正题......【从一个坑跳到另一个坑🐶】
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name '': Bean with name '' has been injected into other beans [] in its raw version as part of a circular reference,
循环依赖及解决
按照上面说的,在UserService[1号]里又注入了UserService[2号],SpringBean初始化的过程是,在初始化[1号]时发现其依赖[2号],那就先初始化[2号],结果又回到了要初始化[1号]。简单说,这不就是套娃.......没完没了🐶
解决方案呢,我是加了@Lazy注解,这个注解的作用是呢,延迟加载,只有真正是用的时候,才会去真正的初始化。上面说的
初始化[2号]时,会直接创建一个[2号]*,即代理对象,将代理对象注入到[1号]中,直接终止了套娃......当真正调用方法B,执行 thisService.A()时,才会创建[2号]。当然还有其他方案,但是我觉得没有延迟加载简洁,想了解可以看下面的文档。