spring的循环依赖是什么,设计的时候怎么避免,而spring又是怎么解决的?
Posted 栗子~~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring的循环依赖是什么,设计的时候怎么避免,而spring又是怎么解决的?相关的知识,希望对你有一定的参考价值。
文章目录
前言
如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。
而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!!
spring的循环依赖是什么,设计的时候怎么避免,而spring又是怎么解决的?
01 spring是如何解决spring依赖的?
站在spring整个体系的而言,spring 的Bean 它是由一个BeanDefinition来的,就是我们在spring当中有一个叫建模的一个类BeanDefinition,
spring的bean它有一系列比较复杂的生命周期,
1、首先spring容器启动,启动完之后它会去做一个简单的扫描;
2、根据扫描的内容和spring.factories里面的内容,然后把它变成BeanDefinition,并存到BeanDefinitionMap中;
3、然后再去对BeanDefinitionMap做遍历,之后做验证,比如验证是否为单例,是否原型,是否为懒加载等等;
4、验证完之后容器往下执行,获取一遍当前实例化这个类有没有存在单例池当中,有没有提前暴露,提前暴露的话,那么spring的Bean就会开始创建我们这个bean;
5、创建bean,第一步通过推断构造方法这一个过程,如果有多个构造方法,那么推断构造方法,即获取最佳的构造方法;
6、通过反射实例化一个java对象,根据这个java对象,对这个Bean做一些初始化工作,
比如说对这个bean是否要去进行一些BeanDefinition合并,咱们这个容器是否支持循环依赖,如果支持循环依赖的话,它会提前暴露一个当前java对象(半成品)所对应的一个ObjectFactory工厂类,把这个工厂类存到一个 二级缓存中,存在一个map当中;
7、暴露完成之后,它会去做这个属性填充,即自动注入;
8、接下来执行各种aware方法回调,比如BeanNameAware、BeanFactoryAware等等;
9、接下来做生命周期初始化的回调,比如我们加上了@PostConstrout或者xml这种初始化生命周期的回调方法
比如:
调用BeanPostProcessor的初始化的前方法【后置处理器的前处理】.
调用初始化方法
调用BeanPostProcessor的初始化的后方法,在这里进行AOP(动态代理的创建),
如果没有代理进行一些事件发布,这里做的事情比较多,大概就是完成代理,生成事件等等之类的。
10、剩下的就是放在单例池当中;
在这其中spring如何解决循环依赖,它首先对X做一些基本的验证,验证完看一下我们有没有提前暴露(X对应的ObjectFactory工厂类),一直往下走,直到把X实例出来之后走到当前容器是否需要支持循环依赖,即将X对应的ObjectFactory工厂类提前暴露,当前容器默认的情况下都需要提前暴露,X就会提前暴露,注意的是这个时候暴露的并不是一个单纯的X,它暴露的是由X生成的,创建出来的一个ObjectFactory工厂类对象,之后往下执行,当执行到对X的属性填充时,发现X里面的Y的时候,就会填充Y,填充时发现Y并没有存在我们这个spring容器中,就会走Y的生命周期,同样的从BeanDefinitionMap当中把Y拿出来,过程跟X的过程一样,只到属性填充Y这一步,在对Y里面的X进行属性填充时,发现X也并不在我们这个spring容器中,因为这个流程是在X走到一半的时候走到这里,因此X并不在我们这个单例池中,然后又会走X的生命周期流程,又重复了一遍,在判断X是否被提前暴露这个环节时,发现X已经提前暴露了,所以它就能拿到我们这个X对应的ObjectFactory工厂类对象。
02 问什么我们的spring循环依赖只支持单例?
如果spring 循环依赖不支持单例,那么我们第一遍我们X压根就不会走生命周期流程,因为我们单例的话,它会在spring容器初始化的时候走我们的Bean生命周期流程,如果我们是原型的话,它一开始不会走的,只有用到的时候才会走spring bean 生命周期,所以不会出现循环依赖这种情况。
03 发生循环依赖的错误代码
当一个bean A依赖于另一个bean B,并且bean B也依赖于bean A时,就会发生这种情况:
Bean A → Bean B → Bean A
因为当Spring上下文加载所有bean时,当具有循环依赖关系时,Spring无法决定应首先创建哪个bean,因为它们相互依赖。 这种情况下,Spring在加载上下文时将引发BeanCurrentlyInCreationException异常。
下面是两个相互依赖的bean例子:
@Component
public class TestA
private TestB testB;
@Autowired
public TestA(TestB testB)
this.testB = testB;
@Component
public class TestB
private TestA testA;
@Autowired
public TestB(TestA testA)
this.testA = testA;
测试:
制作一个TestConfig类,指定要扫描组件的基本程序包,并写一个单元测试来进行测试启动:
@Configuration
@ComponentScan(basePackages = "com.example.demo.test" )
public class TestConfig
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class )
public class TestConfigTest
@Test
public void test_testConfig_1_0()
结果:
04 推荐下面几种方式解决循环依赖问题
1、重新设计;
2、 @PostConstruct:
打破周期的另一种方法是在一个bean上使用@Autowired注入依赖项,然后使用带有@PostConstruct注释的方法设置另一个依赖项:
@Component
public class TestA
@Autowired
private TestB testB;
@PostConstruct
public void init()
testB.setTestA(this);
public TestB getTestB()
return this.testB;
@Component
public class TestB
private TestA testA;
public void setTestA(TestA testA)
this.testA = testA;
public String test()
return "1";
@Configuration
@ComponentScan(basePackages = "com.example.demo.test" )
public class TestConfig
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class )
public class TestConfigTest
@Autowired
ApplicationContext context;
@Bean
public TestA testA()
return new TestA();
@Bean
public TestB testB()
return new TestB();
@Test
public void test_testConfig_1_0()
TestA testA = context.getBean(TestA.class);
Assert.assertEquals("1",testA.getTestB().test());
效果:
3、@Lazy注解
Spring懒惰地初始化一个bean。 即创建一个代理以将其注入到另一个bean中。 注入的Bean仅在首次使用时才完全创建。
@Component
public class TestA
private TestB testB;
@Autowired
public TestA(TestB testB)
this.testB = testB;
@Component
public class TestB
private TestA testA;
@Autowired
public TestB(TestA testA)
this.testA = testA;
测试:
制作一个TestConfig类,指定要扫描组件的基本程序包,并写一个单元测试来进行测试启动:
@Configuration
@ComponentScan(basePackages = "com.example.demo.test" )
public class TestConfig
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class )
public class TestConfigTest
@Test
public void test_testConfig_1_0()
结果:
以上是关于spring的循环依赖是什么,设计的时候怎么避免,而spring又是怎么解决的?的主要内容,如果未能解决你的问题,请参考以下文章