# spring 循环依赖

# 何为循环依赖?

如上图,某应用中的依赖关系

  1. ServiceA -> ServiceC -> ServiceB
  2. ServiceB -> serviceD -> serviceC

其中,ServiceCServiceB形成了循环依赖。

spring中,bean的创建顺序是从依赖到被依赖方,例如:

BeanA -> BeanB -> BeanC

spring会一次创建BeanCBeanBBeanA。 一旦形成循环依赖,则spring无法判断出该按何种顺序创建bean,并抛出BeanCurrentlyInCreationException异常。

# 案例实战

@Service
public class ServiceA {

    private ServiceB serviceB;

    @Autowired
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}
@Service
public class ServiceB {

    private ServiceA serviceA;

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

当项目中存在以上两个service时,服务启动便会报错:

BeanCurrentlyInCreationException: Error creating bean with name 'serviceA':
Requested bean is currently in creation: Is there an unresolvable circular reference?

# 解决方案

# 重构循环依赖

循环依赖往往使我们设计上有问题,公共能力需要提取到单独的Service中,但是由于时间、人力成本、历史等多种原因,重构往往不是最佳选择方案。

# 使用setterfield注入

@Service
@Data
public class ServiceA {

  @Autowired
  private ServiceB serviceB;

}

通过setterfield注入,被依赖的bean会在需要的时候再注入进去。

# @Lazy

@Service
public class ServiceA {

  private ServiceB serviceB;

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

通过@Lazy实现懒注入,在创建ServiceA时首先会注入一个代理对象,真正的ServiceB会在需要用到的时候才创建。

# @PostConstruct

通过@PostConstruct手动在创建好Bean的时候进行注入,避免形成相互依赖。

@Service
public class ServiceA {

  private ServiceB serviceB;

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

  @PostConstruct
  public void init() {
    serviceB.setServiceA(this);
  }
}
@Service
@Data
public class ServiceB {

    private ServiceA serviceA;

}

# ApplicationContextAware 与 InitializingBean

@Service
public class ServiceA implements ApplicationContextAware, InitializingBean {
  private ApplicationContext context;
  private ServiceB serviceB;

  @Override
  public void afterPropertiesSet() throws Exception {
    serviceB = context.getBean(ServiceB.class);
  }

  @Override
  public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
    context = ctx;
  }
}
@Service
public class ServiceB {

    private ServiceA serviceA;

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

# 总结

spring中有很多种解决循环依赖的方案,但是如果可能的话,尽量避免循环依赖。

the end
最后更新: Wed, 09 Aug 2023 14:44:10 GMT

0 评论

加载中...
访问量:-