Spring Boot循环依赖,及失败时的解决方案

循环依赖
        说循环依赖之前,先简单说一下Spring Boot中Bean注入与创建这两步的流程。假设AB互相依赖,Spring Boot先扫到A,那么AB创建及注入流程是这样的:

创建A对象 —> 查找A依赖—> 发现A依赖B —> (创建B实例 —> 查找B依赖—>发现B依赖A —> 注入A实例 —> B初始化完成)—>  为A注入B实例。

        所以在这个流程中,基于filed的注入或setter方法的注入都不会产生问题;但是当采用构造函数注入时,就有可能产生问题(不是一定,原因在下面解答),这是因为对象在构造函数未执行完之前,为了保证对象的完整性,外部通常是无法获取其引用的,以防止对象逸出(其实这也是单例中为什么要加volita的原因,保证顺序性)。

        在Spring Boot中,会记录这种处于构造过程中的实例的名称,每次创建对象时,先检测该对象是否处于“创建中”。由于Spring Boot启动时通过单线程加载类,所以如果某个要创建的对象处于创建中,则说明存在循环依赖。

相关源码如下:

----DefaultListableBeanFactory.class
    public void preInstantiateSingletons() throws BeansException {
        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) {
            //...
            getBean(beanName);...	
        }
    }  

----AbstractAutowireCapableBeanFactory.class     
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
        //...
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args); //创建Bean
        }
        //...Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);  //注入依赖
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        //...
    } 

循环依赖--例1:下面的orderService与goodsItemService互相依赖,注入方式为field注入。经测试下面这种形式的循环依赖,在Spring Boot中不会产生任何问题,Spring Boot能正常启动。上面的流程能正常走完。

@Service
public class OrderService {
    @Autowired
	private GoodsItemService goodsItemService;
}

@Service
public class GoodsItemService {
	@Autowired
	private  OrderService orderService;
}

循环依赖--例2:将上述orderService中goodsItemService的注入方式改成构造器注入。

@Service
public class OrderService {	
	private GoodsItemService goodsItemService;	
	@Autowired
	private OrderService (GoodsItemService goodsItemService) {
		this.goodsItemService = goodsItemService;
	}
}

  此时启动Spring Boot有可能会出现下面的异常,注意只是有可能并不是一定,这取决于这两Java类在你机器上所处的物理位置,因为Spring Boot是通过扫包找到类进行加载,创建及初始化的。所以如果Spring Boot先创建goodsItemService<B>则不会有这个问题。

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  goodsItemService defined in file [D:\project-workspace\springboot-test\target\classes\com\sb\GoodsItemService.class]
↑     ↓
|  orderService (field private com.sb.GoodsItemService com.sb.OrderService.orderService)
└─────┘

循环依赖解决方案

  1)修改注入方式,尽量不要使用基于构造函数的注入。比如Setter、Field、@PostConstruct等注入方式

  2)如果一定要使用构造函数的注入,可以加上@Lazy注解,先注入代理对象,当首次使用时再创建对象完成注入。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值