19.什么是循环依赖?如何解决循环依赖?

159 阅读2分钟

解决循环依赖&bean实例化过程

什么是循环引用?

下面的代码中,两个类相互引用,就是循环依赖。

@Component
public class OrderService {
​
    @Autowired
    UserService userService;
    
    public OrderService(){
        System.out.println("OrderService——>"+userService);
    }
}
​
@Component
public class UserService {
​
    @Autowired
    OrderService orderService;
​
    public UserService(){
        System.out.println("UserService->" + orderService);
    }
}
​
/***
但是如果分别指定类的构造方法为以下形式 就无法解决。
因为通过构造方法传入要求必须传入一个实例化后的对象。
OrderService(UserService   userService );
UserService(OrderService orderService);
***/
public class SpringTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new 
            						AnnotationConfigApplicationContext();
        context.register(Config.class);
        context.refresh();
    }
}

bean的实例化是在refresh()——>finishBeanFactoryInitialization(beanFactory);方法里完成的。

该方法只能实例化单例的类。

这两个类非常简单,就是相互引用了对方,也就是我们常常的说的循环依赖,spring是允许这样的循环依赖。

前提是1.单例的情况下的,2.非构造方法注入的情况下

spring默认是支持循序依赖的,但是仅仅是单例的类才可以。

上面代码从容器中能正常获取到bean,说明循环依赖成功。

但是spring的循环依赖其实是可以关闭的,spring提供了api来关闭循环依赖的功能。

spring也可以关闭循环依赖:

public class SpringTest {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context 
            				= new AnnotationConfigApplicationContext();
        //获取bean工厂
        DefaultListableBeanFactory beanFactory = 
            		(DefaultListableBeanFactory) context.getBeanFactory();
        //关闭循环依赖
        beanFactory.setAllowCircularReferences(false);
        context.register(Config.class);
        context.refresh();
        System.out.println(context.getBean("orderService"));
    }
}

运行上面的代码,就会报错

image.png

那么为什么setAllowCircularReferences(false);会关闭循环依赖呢?

首要明白spring的循环依赖是怎么做到的呢?spring源码当中是如何处理循环依赖的?

分析一下所谓的循环依赖其实无非就是属性注入,或者就是大家常常说的自动注入, 故而搞明白循环依赖就需要去研究spring自动注入的源码。

Spring的属性注入属于spring bean的生命周期一部分;怎么理解spring bean的生命周期呢?

注意笔者这里并不打算对bean的生命周期大书特书,只是需要读者理解生命周期的概念,细节下一篇文章再写。

解决循环依赖的2个前提

单例

非构造方法注入

Spring循环依赖不能解决的问题

不支持多例

不支持构造方法注入的bean

如何解决循环依赖

1.@Autowired自动注入

@Component
public class A {
	@Autowired
	B b;
}

@Component
public class B {
	@Autowired
	A a;
}

2.set方法注入

@Component
public class A1 {
	B1 b;
	@Autowired
	public void setB1(B1 b) {
		this.b = b;
	}
}


@Component
public class B1 {
	A1 a;
	@Autowired
	public void setA(A1 a) {
		this.a = a;
	}
}

3.构造方法循环依赖@Lazy

@Component
public class C {
    D d;
    public C(@Lazy D d) {
        this.d = d;
    }
}
​
@Component
public class D {
    C c;
    public D(@Lazy C c) {
        this.c = c;
    }
}
​

4.构造方法循环依赖无法解决

5.多例循环依赖无法解决

6.解决循环依赖的关键是能够提前暴露对象