对于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对象之前,我们可以尝试思考下,我们自主,如何实现对于依赖对象的注入
现有一个场景,蜜蜂采蜜。蜜蜂是一个对象,蜜蜂采蜜,需要作用于花,因此依赖花的存在。
蜜蜂对象,代码如下:
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,我们如何实现呢?
一 直接简单粗暴地创建对象(可以实现,但是明显不符合自然界对象定义,因为蜜蜂不能创造出花这个对象)
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;
}
}
二 反射获取对象
//采蜜
public void pickingHoney() {
Class clazz = Class.forName("com.example.demo.test.Flower");
Flower flower = (Flower)clazz.getDeclaredConstructor().newInstance();
flower.product();
}
可见,相比第一种来说,第二种反射获取对象,很优雅的实现了对象的处理。
注:反射作为Java的一个典型技术,非常重要
上述示例代码,通过反射实现,获取类对象,然后创建类实例。且上述采用的类名形式,那么我们就可以通过配置文件读取、自定义注解等多种方式,来实现反射的实现。
有了上述的技术积累,我们可以很开心的完成第一步,反射获取对象,实现对象的注入。
再思考,在每个类对象中,处理反射逻辑,会造成代码的冗余,且会造成,对象的创建频繁,没法保证单一等问题。那么,通过学习设计模式中的单例模式、工厂模式,我们可以发现,如果应用上述设计模式,或许更优雅。就这样,我们慢慢贴近了Spring的实现。
Spring框架就是应用了各种设计模式,同时采用反射技术,来实现对象的创建、初始化等操作的。
• Bean对象定义方式
Bean对象的定义,正如我们掌握的,在Spring中,可以通过几种方式完成:
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对象初始化前后的处理
流程描述:
今天我们就先学到此处,明天继续!!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。