项目循环依赖解决

发布时间 2023-11-20 14:16:16作者: stephen·龙

问题:项目启动时报出错误信息,


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

┌─────┐ ````| intermediateService defined in file [E:\projects\business-server\target\classes\com\hyit\business\server\Intermediate\IntermediateService.class] ````↑ ↓ ````| accountTreeServiceImpl defined in file [E:\projects\business-server\target\classes\com\hyit\business\server\service\impl\AccountTreeServiceImpl.class]

以上信息是由于,服务之间出现相互注入,之后产生循环依赖,我开始使用中间服务,进行解决,但是仍然报出错误。

猜想:

由于此项目性能过差,不能在使用懒加载和构造注入来解决问题。

解决方案:

放弃使用自动注入的方式,使用手动依赖查找来满足需求,依赖查找的方式是指,在需要依赖的时候从容器中获取对应的 Bean 对象,而不是在构造函数或setter方法中注入。具体方法是创建一个对象工厂,然后从 BeanFactory 中获取需要的依赖。这种方式可以避免循环依赖。

实现如下:

@Service
public class IntermediateService {

    private final AccountTreeService accountTreeService;
    private final BaseOrgDataAuthService baseOrgDataAuthService;

    public IntermediateService(AccountTreeService accountTreeService, BaseOrgDataAuthService baseOrgDataAuthService) {
        this.accountTreeService = accountTreeService;
        this.baseOrgDataAuthService = baseOrgDataAuthService;
    }

    public AccountTreeService getAccountTreeService() {
        return accountTreeService;
    }

    public BaseOrgDataAuthService getBaseOrgDataAuthService() {
        return baseOrgDataAuthService;
    }
}
@Component
public class IntermediateServiceFactory implements BeanFactoryAware {


    private BeanFactory beanFactory;
    private volatile IntermediateService intermediateService;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }


    /**
     *
     * 暂定使用volatile保证并发和性能问题
     */
    public IntermediateService getIntermediateService() {
        if (intermediateService == null) {
            synchronized (this) {  // 加锁
                if (intermediateService == null) {
                    intermediateService = beanFactory.getBean(IntermediateService.class);
                }
            }
        }
        return intermediateService;
    }
}
关于为什么手动查找依赖对象可以避免并解决相互依赖的问题

在 Spring 容器中,当两个 Bean 互相依赖时,容器必须确定将哪个 Bean 注入给先创建的 Bean。如果采用自动注入或setter方法注入依赖对象,Spring 容器会根据配置的注入顺序和 Bean 定义信息自动完成依赖注入,而这种自动注入或setter方法注入的方式存在循环依赖问题。

当 A 和 B 两个 Bean 互相依赖时,如果采用自动注入或setter方法注入依赖对象,Spring 容器会先创建 A 实例,然后发现 A 需要依赖 B,于是容器又会创建 B 实例。但是创建 B 实例的过程中,发现 B 又需要依赖 A,于是容器又会返回已经创建过的 A 实例,但是此时 A 的依赖 B 尚未被注入完成,导致出现循环依赖的问题。

而采用依赖查找的方式获取依赖对象,则不会出现循环依赖问题。因为依赖查找是由代码手动触发的,可以控制依赖对象的获取顺序和时机。因此,即使存在循环依赖,也可以通过工厂类等手动进行延迟注入,从而避免了循环依赖问题。

总之,手动查找依赖对象可以对依赖关系进行更细粒度的控制,避免了自动注入或setter方法注入方式可能产生的循环依赖问题。但是,手动查找的方式同时也增加了代码的复杂度,需要更多的代码和维护工作,因此需要根据具体情况进行选择。