Spring循环依赖实践

171 阅读1分钟

什么是循环依赖

Bean之间互相依赖,并最终形成了闭环。

自我依赖

@Component
public class C {
    @Autowired
    private C c;
}

互相依赖

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

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

循环依赖使用示例

属性注入

属性的注入通过@Autowired方式注入

	@Component
    public static class AttributeDependOnB {
        @Autowired
        AttributeDependOnA attributeDependOnA;

        public AttributeDependOnA getAttributeDependOnA() {
            return attributeDependOnA;
        }
    }

	@Component
    public static class AttributeDependOnA {
        @Autowired
        AttributeDependOnB attributeDependOnB;

        public AttributeDependOnB getAttributeDependOnB() {
            return attributeDependOnB;
        }
    }
	<!-- 需要开启包扫描 -->
	<context:component-scan base-package="com.ldh"/>

可以正常注入。

setter注入(依赖自身)

 	public static class DependOnSelfEntity {

        DependOnSelfEntity dependOnSelfEntity;

        public DependOnSelfEntity getDependOnSelfEntity() {
            return dependOnSelfEntity;
        }

        public void setDependOnSelfEntity(DependOnSelfEntity dependOnSelfEntity) {
            this.dependOnSelfEntity = dependOnSelfEntity;
        }
    }
	<bean id="dependOnSelf" class="com.ldh.entity.CircularDependencyTestEntity$DependOnSelfEntity">
        <property name="dependOnSelfEntity" ref="dependOnSelf"/>
    </bean>

可以正常注入。

setter注入(互相依赖)

public static class SetterDependOnA {
    SetterDependOnB dependOnB;

    public SetterDependOnA() {
    }

    public SetterDependOnA(SetterDependOnB dependOnB) {
        this.dependOnB = dependOnB;
    }

    public SetterDependOnB getDependOnB() {
        return dependOnB;
    }

    public void setDependOnB(SetterDependOnB dependOnB) {
        this.dependOnB = dependOnB;
    }
}

public static class SetterDependOnB {
    SetterDependOnA dependOnA;
    
	public SetterDependOnB() {
    }

    public SetterDependOnB(SetterDependOnA dependOnA) {
        this.dependOnA = dependOnA;
    }

    public SetterDependOnA getDependOnA() {
        return dependOnA;
    }

    public void setDependOnA(SetterDependOnA dependOnA) {
        this.dependOnA = dependOnA;
    }
}

可以成功注入

构造器注入


    public static class ConstructorDependOnA {
        ConstructorDependOnB constructDependB;

        public ConstructorDependOnA() {
        }

        public ConstructorDependOnA(ConstructorDependOnB constructDependB) {
            this.constructDependB = constructDependB;
        }

        public ConstructorDependOnB getConstructDependB() {
            return constructDependB;
        }
    }

	public static class ConstructorDependOnB {
        ConstructorDependOnA constructDependA;

        public ConstructorDependOnB() {
        }

        public ConstructorDependOnB(ConstructorDependOnA constructDependA) {
            this.constructDependA = constructDependA;
        }

        public ConstructorDependOnA getConstructDependA() {
            return constructDependA;
        }
    }
	<bean id="constructorDependOnB" class="com.ldh.entity.CircularDependencyTestEntity$ConstructorDependOnB">
        <constructor-arg ref="constructorDependOnA"/>
    </bean>
    <bean id="constructorDependOnA" class="com.ldh.entity.CircularDependencyTestEntity$ConstructorDependOnA">
        <constructor-arg ref="constructorDependOnB"/>
    </bean>

测试代码

	@Test
    public void setterCircularDependency(){
        ApplicationContext applicationContext= new ClassPathXmlApplicationContext("circularDependency.xml");
        CircularDependencyTestEntity.SetterDependOnA dependOnA = applicationContext.getBean(CircularDependencyTestEntity.SetterDependOnA.class);
        CircularDependencyTestEntity.SetterDependOnB dependOnB = applicationContext.getBean(CircularDependencyTestEntity.SetterDependOnB.class);
        System.out.println(format("dependOnA:%s",dependOnA));
        System.out.println(format("dependOnA.dependOnB:%s",dependOnA.getDependOnB()));
        System.out.println(format("dependOnB:%s",dependOnB));
        System.out.println(format("dependOnB.dependOnA:%s",dependOnB.getDependOnA()));
    }

报如下错误:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'constructorDependOnB': Requested bean is currently in creation: Is there an unresolvable circular reference?

@Configuration方式测试

	@Configuration
    public static class BeanCircularDependencyConfig {
        @Bean
        public CircularDependencyTestEntity.SetterDependOnB beanCircularDependOnB(){
            CircularDependencyTestEntity.SetterDependOnB dependOnB = new CircularDependencyTestEntity.SetterDependOnB();
            dependOnB.setDependOnA(beanCircularDependOnA());
            return dependOnB;
        }

        @Bean
        public CircularDependencyTestEntity.SetterDependOnA  beanCircularDependOnA() {
            CircularDependencyTestEntity.SetterDependOnA dependOnA = new CircularDependencyTestEntity.SetterDependOnA();
            dependOnA.setDependOnB(beanCircularDependOnB());
            return dependOnA;
        }
    }
	@Test
    public void ConfigurationCircularDependency(){
        AnnotationConfigApplicationContext applicationContext= new AnnotationConfigApplicationContext();
        //注册@Configuration配置类
        applicationContext.register(BeanCircularDependencyConfig.class);
        //必须refresh()
        applicationContext.refresh();
        CircularDependencyTestEntity.SetterDependOnA dependOnA = applicationContext.getBean("beanCircularDependOnA",CircularDependencyTestEntity.SetterDependOnA.class);
        CircularDependencyTestEntity.SetterDependOnB dependOnB = applicationContext.getBean("beanCircularDependOnB",CircularDependencyTestEntity.SetterDependOnB.class);
        System.out.println(format("dependOnA:%s",dependOnA));
        System.out.println(format("dependOnA.dependOnB:%s",dependOnA.getDependOnB()));
        System.out.println(format("dependOnB:%s",dependOnB));
        System.out.println(format("dependOnB.dependOnA:%s",dependOnB.getDependOnA()));
    }

报错:

java.lang.NoClassDefFoundError: org.springframework.beans.FatalBeanException

通过打断点可以发现,Spring陷入死循环,最终报上面的错误

  1. #beanCircularDependOnB()
  2. #beanCircularDependOnA()
  3. #beanCircularDependOnB()
  4. #beanCircularDependOnA()
  5. ......

说明@Configuration方式无法解决循环依赖。

通过增加@Lazy注解也无法解决,报相同的错误

setter注入和构造器注入同时存在的情况

Spring在创建Bean时,默认会根据自然排序进行创建,所以A会先于B创建

  • AB相互依赖 - A为setter,B为构造器:A的半成品加入缓存,然后创建B实例时,即可通过构造器注入完成B的构造
  • AB相互依赖-A为构造器,B为setter:A在创建时需要注入B;创建B时,因为通过构造器注入,A没有用到缓存,创建B失败,导致构造失败

@DependsOn循环依赖

@DependOn注解会显示的指定Bean的依赖关系。但是,如果Bean之间如果通过@DependsOn注解形成了循环依赖的闭环,会抛出异常。

示例代码

    @Configuration
    public static class DependsOnConf {
        @DependsOn("getDependOnEntityB")
        @Bean
        public DependsOnEntityA getDependOnEntityA() {
            return new DependsOnEntityA();
        }

        @DependsOn("getDependOnEntityA")
        @Bean
        public DependsOnEntityB getDependOnEntityB() {
            return new DependsOnEntityB();
        }
    }

错误日志

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getDependOnEntityB' defined in com.ldh.DependsOnTest$DependsOnConf: Circular depends-on relationship between 'getDependOnEntityB' and 'getDependOnEntityA'

	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:305)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)

代码位置

// AbstractBeanFactory

	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        .....
        
            String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
                        // 检查Bean的依赖或传递性依赖的情况
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
            
        .....
    }