03_Spring循环依赖

大厂面试题

请谈谈Spring循环依赖?

解释下Spring的三级缓存?

三级缓存分别是什么?三个map有什么异同?

什么是循环依赖?请你谈谈?看过Spring源码吗?一般我们说的spring容器是什么?

如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?

多例的情况下,循环依赖问题为什么无法解决?

什么是循环依赖

多个bean之间相互依赖,形成一个闭环。比如:A依赖于B、B依赖于C、C依赖于A;

通常来说,如果问Spring容器内部如何解决循环依赖,一定是指默认的单例Bean中,属性互相引用的场景。

public class Test
{
    class A{
        B b;
    }
    class B{
        C c;
    }
    class C{
        A a;
    }
}

在这里插入图片描述

两种注入方式对循环依赖的影响

循环依赖官网说明

Circular dependencies

If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.

For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.

One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.

Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).

结论:

我们AB循环依赖问题只要A的注入方式是setter且singleton,就不会有循环依赖问题。

Spring容器循环依赖报错演示

循环依赖两种情况

循环依赖现象在Spring容器中注入依赖的对象,有两种情况:

①构造器方式注入依赖

代码:

public class ServiceA {
    private ServiceB serviceB;

    public ServiceA(ServiceB serviceB){
        this.serviceB = serviceB;
    }
}

public class ServiceB {

    private ServiceA serviceA;

    public ServiceB(ServiceA serviceA){
        this.serviceA = serviceA;
    }
}

public class ClientConstructor {
    public static void main(String[] args) {
        //无法解决循环依赖的
        new ServiceA(new ServiceB(new ServiceA()))
    }
}

在这里插入图片描述

结论:构造器循环依赖是无法解决的,你想让构造器注入支持循环依赖,是不存在的。

②set方法注入依赖

代码:

public class ServiceAA {
    private ServiceBB serviceBB;

    public void setServiceBB(ServiceBB serviceBB) {
        this.serviceBB = serviceBB;
        System.out.println("A 里面设置了 B");
    }
}

public class ServiceBB {
    private ServiceAA serviceAA;
    public void setServiceAA(ServiceAA serviceAA){
        this.serviceAA = serviceAA;
        System.out.println("B 里面设置了 A");
    }
}

public class ClientSet {
    public static void main(String[] args) {
        ServiceAA serviceAA = new ServiceAA();
        ServiceBB serviceBB = new ServiceBB();
        serviceAA.setServiceBB(serviceBB);
        serviceBB.setServiceAA(serviceAA);
    }
}

执行结果:

A 里面设置了 B
B 里面设置了 A

Process finished with exit code 0

结论:set注入可以解决循环依赖的问题

重要Code案例演示

java基础编码
public class A {
    private B b;
    public A(){
        System.out.println("A 创建了对象......");
    }

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}

public class B {
    private A a;
    public B(){
        System.out.println("B 创建了对象.....");
    }

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
}

public class ClientCode {
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        a.setB(b);
        b.setA(a);
    }
}
spring容器

默认单例的场景是支持循环依赖的,不报错;

原型prototype的场景是不支持循环依赖的,会报错

步骤:

① 引入applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--  循环依赖测试案例  -->
    <bean id="a" class="com.interview.ujiuye1.spring.A">
        <property name="b" ref="b"/>
    </bean>
    <bean id="b" class="com.interview.ujiuye1.spring.B">
        <property name="a" ref="a"/>
</bean>

②默认单例,修改为原型scope=“prototype”

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--  循环依赖测试案例  -->
    <bean id="a" class="com.interview.ujiuye1.spring.A" scope="prototype">
        <property name="b" ref="b"/>
    </bean>
    <bean id="b" class="com.interview.ujiuye1.spring.B" scope="prototype">
        <property name="a" ref="a"/>
</bean>

③ClientSpringContainer

public class ClientConstructor {
    public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        A a = context.getBean("a", A.class);
        B b = context.getBean("b", B.class);
    }
}

④循环依赖异常

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?  #不能解析的循环依赖
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:267)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
	... 17 more

重要结论:

Spring内部通过3级缓存来解决循环依赖: DefaultSingletonBeanRegistry

只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。

第一级缓存:也叫单例池singletonObjects,存放已经经历了完整生命周期的Bean对象

第二级缓存:earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束,属性还为填充完

第三级缓存:Map<String, ObjectFactory<?>> singletonFactories,存放可以生成Bean的工厂

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	/** Maximum number of suppressed exceptions to preserve. */
	private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;


	/**一级缓存 Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/**三级缓存 Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/**二级缓存 Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

	/** Set of registered singletons, containing the bean names in registration order. */
	private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值