首页 > 其他分享 >浅谈循环依赖

浅谈循环依赖

时间:2024-03-29 16:44:17浏览次数:17  
标签:依赖 浅谈 Autowired private 循环 引用 public

说明

  • 循环依赖是一个大家讨论很多的话题,它更多是一个工程上的问题而不是技术问题,我们需要首先有一定的认知:

    • 如同两个人相互帮忙,两个类之间你调用我的,我调用你的是很正常也很自然的需求模型。
    • 单一依赖确实有好处,改动一个最顶层类时不需要在意对底部类的影响,但是从本来就自然的模型非要理顺的话就需要额外付出代价,例如额外的拆分类。
  • 循环依赖可以分这几种:

    • 从小的来说是类之间的相互引用。
    • 再大一点的来说是同一个项目下不同模块之间的引用。
    • 再再大一点的来说涉及到微服务或不同类库之间引用。

    对于微服务级别或是模块级别的引用来说解决循环依赖是有必要的,因为这可能牵扯到不同人分工协作的问题,而类之间尤其是同一模块下的类之间是否禁止循环依赖实际上是有争议的,本文只讨论同一模块下的类之间的循环引用。

  • 一些解决循环依赖的方法类似@LazygetBean等实际上解决的不是循环依赖,而是解决的springboot启动时的循环依赖检测,但本质上它们还是相互引用,所以这里不讨论这些方法,只讨论拆分类的方法。

  • 个人目前比较认同的是同一个人写的同一个模块下的功能是可以循环依赖的,增加额外的拆分类反而会增加复杂度及影响效率,但springboot 2.6版本之后默认禁止了循环依赖,所以个人也在思考,如果想要拆分的化要怎么拆分,目前总结了如下两种不同类型的循环引用拆分示例。

示例

情形一
  • 最常见的老师学生这种或是主表子表相互关联的:

    @Component
    public class Teacher {
        @Autowired
        private Student student;
        
        public void method() {
            //获取某教师下学生类别
            List<String> students = student.getStudentsByTeacher("xxx");
            System.out.println(students);
        }
    }
    
    @Component
    public class Student {
        @Autowired
        private Teacher teacher;
        
        public void method() {
            //获取学生归属的教师
            String teacherStr = teacher.getTeacherByStudent("xxx");
            System.out.println(teacherStr);
        }
    }
    
  • 这种拆分比较简单,类似数据库多对多的中间表,我们也创建一个中间类,然后TeacherStudent类不要依赖彼此,直接抽取方法到中间类中或是都引用中间类:

    @Component
    public class TeacherStudent {
        @Autowired
        private Teacher teacher;
        @Autowired
        private Student student;
        
         public void method1() {
            //获取某教师下学生类别
            List<String> students = student.getStudentsByTeacher("xxx");
            System.out.println(students);
        }
        
        public void method2() {
            //获取学生归属的教师
            String teacherStr = teacher.getTeacherByStudent("xxx");
            System.out.println(teacherStr);
        }
    }
    
情形二
  • 另一种常用的场景是引用第三方类库A,然后在配置类B中用@Bean来实例化,而类A是通过读取数据库中的配置(通过类C)来组装参数,而当数据库配置变更时(类C中更新),由于参数变化同时也要重置类A实例,我们在集成微信、钉钉等SDK时会经常遇到此情况,如果直接按照此逻辑写的话,就是下述的代码:

    //B本身是个配置类
    @Configuration
    class B { 
        @Autowired
        private C c;
    
        @Bean 
        public A init(){
            A a = new A();
            //引用c的数据库中数据来组装成A实例
            a.setProp(c.getProp());
            return a;
        }
    }
    
    @Component
    class C {
        @Autowired
        private A a;
    
        public void update(){
            //修改数据库相关后,又来重置A实例
            a.reset();
        }
    }
    
  • 此情况下最主要的耦合就是类A需要类C的数据来作为配置项,所以把这个耦合独立出来,而类B中去除类C的引用,仅仅是生成类Abean

    //用@PostConstruct
    @Component
    public class D {
        @Autowired
        private A a;
        @Autowired
        private C c;
        
        @PostConstruct
        public void init(){
            a.setProp(c.getProp());
        }
    }
    //或是@Autowired注解到方法上
    @Component
    public class D {
        @Autowired
        public void init(A a, C c) {
            a.setProp(c.getProp());
        }
    }
    
  • 和第一种情况不同的是类A是一个第三方的类库,我们无法修改其引用及方法,而其本身并不是个bean,需要我们额外去操作。

结果

  • 上述的情况都是额外增加一个拆分类来处理,这样无形中增加了代码量,尤其是第一种情形太常见了,除非是项目初始时就规定好禁止service层互相调用,而是单独再划分一层来处理(类似阿里的manager层),否则的话个人宁愿用@Lazy来解决掉循环依赖的报错。
  • 解决循环依赖上述同模块内的相对简单些,只是增加代码量而已,当涉及到模块或微服务时,则完全不一样,要考虑业务逻辑及架构等一系列问题,感觉很是麻烦。
  • 以上只是个人见解,有更好的观点可发到评论区一起讨论下。

标签:依赖,浅谈,Autowired,private,循环,引用,public
From: https://www.cnblogs.com/vishun/p/18104143

相关文章

  • 浅谈数据治理之道 数据运用(四)
    前面我们谈到了数据分析,数据运用是数据分析的延伸,是将分析得出的洞见转化为实际行动的过程。那么我们需要进一步的将分析出来的数据进行运用,这也是非常关键的一个动作,也是数据价值所在。只有用数据来发现和解决问题,数据就变得非常有用了。那么面对从海量数据中提取价值、确保......
  • 浅谈WPF之属性系统
    在WPF开发中,经常听到各种属性,如:依赖属性,附加属性,CLR属性,那这些不同类型的属性,具体又有什么作用呢?今天以一些简单的小例子,简述一下WPF开发中,各种属性的相关概念和应用,仅供学习分享使用,如有不足之处,还请指正。 CLR属性 CLR属性(CommonLanguageRuntime),又称为.Net标准属性,是......
  • .NET C#导出解决方案的NuGet依赖关系
    前言公司项目需要写DS设计文档,文档需要标识出来你的解决方案文件下的所有项目都使用了NuGet哪些第三方依赖,我们都知道sln下面的所有.csproj文件中的节点下会标识出对应的依赖,但一个一个对比又太麻烦(主要是懒),有时候一个sln能有10几个project项目,能不能写脚本一键导出这些依赖关......
  • 多值依赖的有关概念
    定义:设R(U)是属性集U上的一个关系模式。X,Y,Z是U的子集,并且Z=U-X-Y。关系R(U)中多值依赖X→→Y成立,当且仅当对R(U)的任一关系r,给定的一对(x,z)值,有一组Y的值,这组值仅仅决定于x的值而与z值无关。定义看起来很抽象,但实际上理解起来一点也不简单。这里还是采用王珊老师的《数据库系......
  • day10:字典的循环遍历及序列操作
    一、字典的循环遍历1.遍历字典的键dict1={'name':'张三','age':20,'gender':'男'}forkeyindict1.keys():print(key)#name#age#gender2.遍历字典的值dict1={'name':'张三','age':20,'......
  • 浅谈C# Linq里的FirstOrDefault,First,Single,SingleOrDefault 方法
    FirstOrDefault:返回第一个元素,如果为空,则返回类型的默认值;数值类型默认值是0,引用类型默认值是NULL,布尔类型默认值是FalseFirst:也是返回第一个元素,但是如果为空的话,会抛出异常!!Single:返回唯一一个符合条件的元素,如若没有或者有多条,都会抛出异常!SingleOrDefault:返回唯一一个......
  • Linux服务器上安装依赖报错No space left on device
    在安装anaconda到/home/xxx时无法正常安装,根据以下报错信息到网上查询之后发现是内存空间不足导致的,使用df命令查看安装前/home目录下还有4G空间,但是报错后只剩下了60多MB。[53453]Failedtoexecutescript'entry_point'duetounhandledexception!改装miniconda,minico......
  • ffmpeg学习window下使用Visual Studio创建cpp项目添加ffmpeg源代码编译好的依赖库
    ffmpeg学习window下使用VisualStudio创建cpp项目添加ffmpeg源代码编译好的依赖库1.创建cpp项目启动VisualStudio,创建新项目选择控制台运用程序随便输入一个项目名称,点击创建,完成helloworld项目的创建编译和运行项目,按f7编译项目,按f5运行项目下次重新打开......
  • 农村分散式生活污水分质处理及循环利用技术指南
    标准已完成意见征集:本文件给出了农村分散式生活污水分质处理及循环利用的总则、污水收集、污水分质处理、资源化利用、利用模式、运维管理等的指导。本文件适用于农村分散式生活污水分质处理及循环利用的设施新建、扩建和改建工程的设计、施工与运维。注:本文件使用时宜综......
  • 头歌python循环结构答案
    Python中的循环结构,并提供一些常见的循环结构示例。在Python中,有两种主要的循环结构:`for`循环和`while`循环。###`for`循环`for`循环通常用于遍历序列(如列表、元组、字符串)或其他可迭代对象。**示例1:遍历列表中的元素**```pythonfruits=['apple','banana','cherry'......