前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试必问系列之最强源码分析,带你一步步弄清楚Spring如何解决循环依赖(一)

面试必问系列之最强源码分析,带你一步步弄清楚Spring如何解决循环依赖(一)

原创
作者头像
huc_逆天
发布2024-01-21 23:31:52
1691
发布2024-01-21 23:31:52

对于Spring框架或者Springboot框架使用的小伙伴们,不管是面试还是实际工作中,面临的一个非常频繁的问题就是如何解决循环依赖。本文就是从源码分析角度,一步步弄清楚循环依赖的前世今生,以及如何正确的有效解决它!

写在前面

考查源码时必然会问到的一个面试题 - -Spring循环依赖是如何解决的。今天,我们就来好好分析下这个话题,我会很细致的讲解。预计,本文会很长很长,希望大家有耐心,去读完,相信你读完之后,对于Spring的IOC部分将有更深的源码理解。采用我的逻辑去把这个问题去解读明白。大致分为以下几部分:

• 何为循环依赖?

• Spring管理bean对象

• Spring如何解决循环依赖

何为循环依赖

想要理解这个问题,那么首先呢,需要有基础的知识储备。那就是Spring的IOC。IOC,是控制反转,后来出现更容易的理解 DI,依赖注入。大致上就是,一个A对象内有一个B对象属性,无需A对象显式创建B对象,可以通过Spring容器进行注入B对象到A对象中。这便是依赖注入的含义。

循环依赖的出现,是因为A对象中依赖B对象作为其中属性,B对象中依赖A对象作为其中属性。如下图所示:

举个例子描述:

小明喜欢小红,小红喜欢小黑,小黑喜欢小丽,小丽喜欢小明,如果中间所有人都不放弃喜欢的人,那么每个人都将陷入爱情循环中,无法自拔。

类似于这种依赖关系,在自然界中屡见不鲜,因此,映射到我们java的对象世界中,就必然会存在。

那么,如何处理循环依赖,保证合理的程序运行,是我们需要思考的问题。Spring作为一个技术框架,必然考虑到了循环依赖的问题。

Spring管理Bean对象

理解Spring作为容器管理bean对象之前,我们可以尝试思考下,我们自主,如何实现对于依赖对象的注入

场景描述

现有一个场景,蜜蜂采蜜。蜜蜂是一个对象,蜜蜂采蜜,需要作用于花,因此依赖花的存在。

蜜蜂对象,代码如下:

代码语言:java
复制
public class Bee {
   /**
    * 性别
    */
   private String sex;
   /**
    * 年龄
    */
   private Integer age;
   @Autowired
   private Flower flower;
   /**
    * 采蜜
    */
   public void pickingHoney() {
       // 花产蜜
       flower.product();
  }
   public String getSex() {
       return sex;
  }
   public void setSex(String sex) {
       this.sex = sex;
  }
   public Integer getAge() {
       return age;
  }
   public void setAge(Integer age) {
       this.age = age;
  }
}

上述代码,是Spring采用注解注入的方式实现。

那么,试想,如果不使用Spring,我们如何实现呢?

一 直接简单粗暴地创建对象(可以实现,但是明显不符合自然界对象定义,因为蜜蜂不能创造出花这个对象)

代码语言:java
复制
public class Bee {
   /**
    * 性别
    */
   private String sex;
   /**
    * 年龄
    */
   private Integer age;
   /**
    * 采蜜
    */
   public void pickingHoney() {
       // 花产蜜
       Flower flower = new Flower();
       flower.product();
  }
   public String getSex() {
       return sex;
  }
   public void setSex(String sex) {
       this.sex = sex;
  }
   public Integer getAge() {
       return age;
  }
   public void setAge(Integer age) {
       this.age = age;
  }
}

二 反射获取对象

代码语言:java
复制
    //采蜜
   public void pickingHoney() {
      Class clazz = Class.forName("com.example.demo.test.Flower");
       Flower flower = (Flower)clazz.getDeclaredConstructor().newInstance();
       flower.product();
  }

可见,相比第一种来说,第二种反射获取对象,很优雅的实现了对象的处理。

注:反射作为Java的一个典型技术,非常重要

上述示例代码,通过反射实现,获取类对象,然后创建类实例。且上述采用的类名形式,那么我们就可以通过配置文件读取、自定义注解等多种方式,来实现反射的实现。

有了上述的技术积累,我们可以很开心的完成第一步,反射获取对象,实现对象的注入。

再思考,在每个类对象中,处理反射逻辑,会造成代码的冗余,且会造成,对象的创建频繁,没法保证单一等问题。那么,通过学习设计模式中的单例模式、工厂模式,我们可以发现,如果应用上述设计模式,或许更优雅。就这样,我们慢慢贴近了Spring的实现。

Spring框架就是应用了各种设计模式,同时采用反射技术,来实现对象的创建、初始化等操作的。

Spring管理bean对象

• Bean对象定义方式

Bean对象的定义,正如我们掌握的,在Spring中,可以通过几种方式完成:

XML文件定义

代码语言:xml
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
   <bean id="a" class="com.example.demo.test.A">
       <property name="b" ref="b"></property>
   </bean>
   <bean id="b" class="com.example.demo.test.B">
       <property name="a" ref="a"></property>
   </bean>
</beans>

如代码展示,可以直接通过xml文件,管理bean对象,实现bean对象的定义,以及属性的注入

注解形式可以通过各类注解,比如@Component @Controller @Service 等等

• Bean对象读取

在Spring管理下,对于Bean对象的读取,会形成一个抽象层,BeanDefinitionReader,然后形成BeanDefinition,完成对于Bean对象描述的定义

• BeanFactory

Bean对象的创建,由创建工程BeanFactory完成。BeanFactory会完成BeanDefinition的转换,然后构建bean对象,完成bean对象的实例化、初始化。

工厂,通过反射技术,根据BeanDefinition创建Bean对象。

• FactoryBean

FactoryBean是特殊的BeanFactory

• BeanFactoryPostPocessor

BeanFactory的后置处理器,完成BeanFactory创建后的一系列补充

• BeanPostPocessor

Bean对象初始化前后的处理

流程描述:

  1. Spring读取XML文件等形式对于Bean对象的定义,构建BeanDefinition
  2. Spring的BeanFactory,读取BeanDefinition,完成Bean对象的读取,通过反射技术进行实例化
  3. BeanFactoryPostPocessor作为BeanFactory的后置处理器,完成补充处理
  4. BeanPostPocessor - before 完成对于bean对象初始化前的对象处理
  5. BeanPostPocessor - after完成对于bean对象初始化后的对象处理
  6. context.getBean 进行bean对象的使用 通过以上的分析,我们掌握了,Spring对于bean对象的读取、创建实例化、初始化的过程。了解了这些,我们才能去看循环依赖的事情。如若不然,个人觉得理解循环依赖问题,就比较困难。

今天我们就先学到此处,明天继续!!

我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • 何为循环依赖
  • Spring管理Bean对象
    • 场景描述
      • Spring管理bean对象
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档