`
yiminghe
  • 浏览: 1466379 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

circular dependency

 
阅读更多

循环依赖是和语言无关的一个设计问题,在各个语言环境下都可能存在循环依赖,甚至引起 stackoverflow,这种大多数是由于不好的设计而导致的,一般来说都可以通过提取公共模块而解决,特定的场景下对应用程序做一定的调整也可以在维持循环依赖的前提下避免栈溢出。

 

语言机制:

 

c++

 

c++ 中的循环类定义引用会导致编译出错,一般的解决方案可以通过提前声明来解决:

 

 

b.h
class A;
class B {
  A a;
}

a.h
class B;
class A {
  B b
}
 

 

java

 

java 不需要像 C++ 一样提前声明,其中的循环依赖一般指两个类中实例变量的循环初始化,例如:

 

 

class A {
  B b=new B();
}

class B {
  A a =new A();
}
 

但是有了 spring 就可以很方便地通过依赖反转来解决这个问题,对应用代码做少许改变:

 

 

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

class B{
 A a;
 void setB(A a){this.a=a;}
}
 

将依赖信息告诉 spring,spring 会设置好两个类实例的对应成员变量(先初始化实例再依赖注入)。

 

 

python

 

循环依赖指模块间的循环 import ,模块间互相使用对方的功能,在某些情景下其实可以通过调整引用顺序而避免引用不到对应模块的功能。

 

 

x.py

import y

def z():
  y.xx();

def q():
  // 


y.py

import x

x.q() //=> 没有 q
 

可以看到 y 的初始化需要立即使用 x模块的功能,而 x 模块初始化并不立即需要 y 模块功能,那么可以通过调整 x 模块中 y 模块的引入位置来避免初始化依赖而导致的空函数

 

 

 

x.py

def z():
  y.xx();

def q():
  //

import y
 

 

javascript

 

javascript 没有原生的模块机制,如果遵从 commonjs modules/1.1.1  ,那么面临和 python 一样的问题:

 

 

// a .js
define(function(require,exports){
  var b=require("b");  
  exports.z=function(){ b.z(); }
  exports.y=function(){}
})

// b.js
define(function(require,exports){
   var a=require("a");
   exports.z=function(){};
   expots.x=a.y();
})
 

 

这种情况下的解决也是类似的调整代码顺序:

 

 

// a .js
define(function(require,exports){
  exports.z=function(){ b.z(); }
  exports.y=function(){}
  var b=require("b");  
})
 

 

而对于 amd (requirejs , kissy ) 形式的模块则无论哪种形式的循环引用都不可能通过简单的调整代码顺序来解决:

 

 

// a .js
define(["b"],function(b){
  var ret={};
  ret.z=function(){ b.z(); }
  ret.y=function(){}
  return ret;
})

// b.js
define(["a"],function(a){
   var ret={};
   ret.z=function(){};
   ret.x=a.y();
   return ret;
})
 

 

 

循环依赖检测

 

程序中出现循环依赖是一种坏味道,所以在出现循环依赖时都希望语言实现以及模块框架能够及时提示,对于客户端的动态加载代码场景,js 的检测方法又有点特殊。

 

 

静态循环检测

 

一般的循环检测是有向图的一个常见问题,最常用的是 floyd 算法,而这种算法的前提是整个依赖图已经都全部知道,而对于 js 模块代码的依赖则是边下载模块脚本边构造模块的依赖图,没有机会等依赖图全部建立好后再进行静态循环分析(存在循环以来时,依赖图实际上永远也建立不好)。

 

动态循环检测

 

对于静态循环依赖检测,floyd 算法在空间和时间两方面都取得了很好的效果,这里提出的在构建依赖图的同时动态检测循环依赖的算法难免在空间方面有所不足。

 

前提:

 

模块 a 的初始化需要在 a 的所有依赖模块初始化之后。

 

步鄹:

 

对每个模块构建 map 结构 all_requires 表示当前模块的所有递归依赖项的集合,那么

 

1. 初始条件下 all_requires 为空 map

 

2. 在两种情况下扩充 all_requires

    2.1 当前模块 a 下载完毕,取得 a 的直接依赖集合 bs,合并 bs 到 all_requires

    2.2 由于模块 b 的初始化而导致作为 b 的依赖模块 a 的初始化:将模块 a 的直接依赖集合 bs 中每个模块的 all_requires 都合并到 a 的 all_requires 中

 

3. 检测当前模块 a 的 all_requires 集合中是否包含 a

    3.1 包含,则 a 的 all_requires 集合中的所有模块形成了循环依赖

 

4. 如果 a 初始化后,a 的 all_requires 仍然没有包含 a ,则 a 不在某个循环依赖模块集合中。

 

举例:

 

a -> b -> c -> a

 

初始 all_requires 集合:

 

a : {}

b: {}

c: {}

 

下载 a 后:

 

a:{b}

b:{}

c:{}

 

下载 b 后

 

a:{b}

b:{c}

c:{}

 

下载 c 后

 

a:{b}

b:{c}

c:{a}

 

c 导致 a 初始化

 

a:{bc}

b:{c}

c:{a}

 

a 导致 b 初始化

 

a:{bc}

b:{ca}

c:{a}

 

b 导致 c 初始化

 

a:{bc}

b:{ca}

c:{abc}

 

结束:

c 发现自己处于 abc 循环依赖中,报错

 

算法复杂度分析:

 

时间复杂度为 o(l), l 为循环依赖模块集合的大小

空间复杂度比较高,为 o(l^2) ,每个模块的 all_requires 集合都包含了近似于所有循环依赖的模块。

 

Refer:

 

http://en.wikipedia.org/wiki/Cycle_detection

 

http://en.wikipedia.org/wiki/Circular_dependency

 

http://effbot.org/zone/import-confusion.htm

 

http://stackoverflow.com/questions/3646113/circular-dependency-in-java-classes

 

http://stackoverflow.com/questions/3485347/circular-dependency-in-spring

 

http://java.dzone.com/articles/tackling-circular-dependency

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论
1 楼 xthsky 2011-12-12  
明白了,多谢

相关推荐

    Circular dependency detected(解决方案).md

    Circular dependency detected(解决方案).md

    Dependency Walker(x86)

    Dependency Walker detects many common application problems such as missing modules, invalid modules, import/export mismatches, circular dependency errors, mismatched machine types of modules, and ...

    depends22_x64

    Dependency Walker detects many common application problems such as missing modules, invalid modules, import/export mismatches, circular dependency errors, mismatched machine types of modules, and ...

    Circular-Class-Dependency

    循环类依赖软件测试项目,2015 年Spring检测Java类中循环依赖的Java程序

    Spring 解决循环依赖的 3 种方式.docx

    1. 构造器参数循环依赖(Constructor-based circular dependency) 这是描述中提到的第一种方式。当Spring检测到构造器参数中的循环依赖时,它会使用“三级缓存”机制来解决。这个机制包括以下三个缓存: - ...

    lcc循环依赖重磅资料

    在IT行业中,循环依赖(Circular Dependency)是一个常见的问题,尤其在后端开发中。"LCC"在这里可能指的是"Loop-Closure Constraint"或者某种特定的处理循环依赖的机制。在这个"lcc循环依赖"的重磅资料中,我们可以...

    spring-learn.zip

    本资源"spring-learn.zip"显然是一份关于学习Spring框架的资料,特别是聚焦于循环依赖(Circular Dependency)这一主题。循环依赖是编程中常见的问题,尤其是在依赖注入(Dependency Injection,DI)框架如Spring中...

    Spring循环依赖案例问题详解.docx

    在Spring框架中,循环依赖(Circular Dependency)是指两个或多个bean之间形成了一种相互引用的关系,使得它们在初始化过程中无法独立完成实例化。在上述案例中,`AuthorService`依赖于`BookService`,而`...

    画图带你彻底弄懂三级缓存和循环依赖的问题.doc

    在 Spring 中,循环依赖(Circular Dependency)和三级缓存是两个重要的概念,尤其在面试和技术探讨中经常被提及。本文将深入探讨这两个主题,帮助你彻底理解它们。\n\n首先,让我们定义什么是循环依赖。在 Java ...

    hibernate一对多,多对一,一对多双向关联

    在实际应用中,我们需要谨慎设计关联关系,避免循环引用(Circular Dependency),这可能导致内存泄漏或者懒加载陷阱。同时,为了优化性能,我们可能需要调整关联的加载策略,比如使用懒加载(Lazy Loading)避免...

    serverless-plugin-split-stacks-circular-dependency-fixer:此插件修复了由无服务器插件拆分堆栈在复杂项目上创建的循环依赖关系

    The CloudFormation template is invalid: Circular dependency between resources: [ApiGatewayUsagePlanKey2, ApiGatewayUsagePlanKey1, WebrtcNestedStack, ApiGatewayDeployment1528886435994, ...

    hrms_project

    人力资源管理系统 (HRMS) 后端想要 :fire: 要求 1:求职者应该能够在系统中注册。 接受条件: ... :rainbow: 各个领域都需要。 通知用户。... :rainbow: 如果验证无效,则通知用户。... :rainbow: 如果有以前

    JavaScript 模块的循环加载实现方法

    “循环加载”(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。 // a.js var b = require('b'); // b.js var a = require('a'); 通常,”循环加载”表示存在强耦合,如果处理不好...

    Contact-Keeper-CRUD-MERN-app

    另一个项目填补了我直到找到实习职位的时间。 具有身份验证功能的完整Crud Mern堆栈...(node:25647) Warning: Accessing non-existent property 'MongoError' of module exports inside circular dependency Server

    循环依赖插件:在使用Webpack编译的模块中检测循环依赖

    循环依赖插件与webpack捆绑时,检测具有循环依赖性的模块。 循环依赖关系在复杂软件中通常是必需的,...基本用法// webpack.config.jsconst CircularDependencyPlugin = require ( 'circular-dependency-plugin' )mod

    CodeSmell漏洞研究

    5. **循环依赖(Circular Dependency)**:模块间的相互依赖形成环状,可能导致系统结构混乱,增加耦合度。 6. **硬编码(Hard Coding)**:将值直接写入代码中,不易于更改和维护,可能导致安全问题。 7. **过度设计...

    Spring 循环引用(三)源码深入分析版

    在Spring框架中,循环引用(Circular Dependency)是一个常见的问题,特别是在使用依赖注入(Dependency Injection)时。本篇文章将深入分析Spring如何处理循环引用,并通过源码解析其内部机制。 首先,我们来理解...

    android module解耦组件化总体概述(推荐)

    在AXXX module中api project(':BXXX'),而在BXXX module中api project(':AXXX'),这样出现了相互依赖,编译器会出现Circular dependency,循环相互依赖的问题。 为了解决上述的问题,将AXXX module与BXXX module...

    Spring循环依赖正确性及Bean注入的顺序关系详解

    在Spring框架中,循环依赖(Circular Dependency)是一个常见的问题,特别是在复杂的系统中。Spring通过其强大的依赖注入(Dependency Injection,DI)机制,有效地解决了这个问题。本文将深入探讨Spring如何处理...

    Spring循环依赖报错Bean with name ‘**’ has been injected into other beans [**] in its raw version as part

    在Spring框架中,循环依赖(Circular Dependency)是指两个或多个Bean之间形成的一种相互依赖关系,导致Spring容器在初始化这些Bean时遇到困难。当一个Bean依赖于另一个Bean,而后者又反过来依赖于前者,就会出现...

Global site tag (gtag.js) - Google Analytics