Spring中什么是Bean及其生命周期

一、什么是Bean

Bean就是Spring的IOC容器中管理的东西,我们通过将类声明为Bean类,那么Spring在启动时就会生成对应的代理Bean对象放到IOC容器中,在程序中需要用到这些类的对象时spring可以直接从容器中获取对应的Bean对象进行依赖注入,而不需要通过new去创建。这样可以很好的实现代码的解耦以及对象的复用。

二、声明Bean的方法

声明Bean的方法主要有如下几种

  1. 使用xml配置文件。不过这种方式使用得越来越少了
  2. 使用@Component及其派生注解比如Controller、Service、Configration,注解在类上,这个类就会作为Bean类交给容器管理
  3. @Import注解,这个主要是用来导入第三方Jar包中的配置类文件spring.factories中的类,类似于spi机制。
  4. 使用@Bean注解,注解在方法上,这个方法返回的对象就会作为Bean对象交给容器管理

三、Bean对象的生命周期

Spring在启动时会通过扫描上述xml配置文件和注解等方式将程序中用到的Bean类创建为BeanDefinition类,放到容器的BeanDefinitionMap中。这个BeanDefinition就包含了创建对应Bean对象所需的信息,包括其原类BeanClass。然后就扫描BeanDefinitionMap开始根据BeanDefinition来创建Bean对象放到IOC容器中,其生命周期如下:

  1. 构造对象:从BeanDefinition中的BeanClass通过反射获取构造方法,若不止一个构造方法则优先拿有@Autowired注解的方法或者无参的构造方法。然后使用构造方法构造一个普通对象,如果是带参的方法,则会在容器单例池map中找参数对应的Bean对象进行传入,没找到则报错。
  2. 依赖注入:将对象中依赖到的其他Bean对象填充到对象中,如果存在循环依赖则通过三级缓存来解决。
  3. 初始化实例: (1) 初始化用户实现Aware接口自定义的容器信息,比如Bean的名字。(2) 初始化用户实现InitializeingBean接口定义的依赖注入后执行的方法。还有Bean上用户自定义的initMethod方法。(3) 在初始化前后还会执行一系列BeanPostProcessors后置处理器,其中就包括了对对象的AOP代理。最后把生成的Bean对象放到IOC容器的单例池map中,此时这个Bean对象就已经可以被使用了。
  4. 注册销毁:将实现了销毁接口DisposableBean的Bean进行注册,Bean销毁时就会执行对应的方法。
  5. 销毁:当spring调用close方法关闭上下文容器时,会对Bean进行销毁。销毁前执行用户通过@PreDestroy注解定义的方法,然后开始逐一销毁Bean中依赖到的其它Bean对象,并执行注册的销毁接口方法,最后执行用户在Bean上自定义的destroyMethod方法。

参考:https://www.bilibili.com/video/BV1584y1r7n6/

四、三级缓存解决循环依赖问题

但是如果一个类A的普通对象在依赖注入时用到了其它Bean对象,则要去单例池中进行查找,如果没有则也要进行创建,如果这个Bean反过来又依赖了A的Bean对象,那么这就构成了循环依赖。

Spring中是通过三级缓存来解决循环依赖的,这三级缓存都是一个Map:

  1. 一级缓存singletonobjects:就是前面的单例池map,用于存储已经创建完成的Bean对象。
  2. 二级缓存earlySingletonObjects:用于缓存提前创建的Bean对象。
  3. 三级缓存singletonFactories:用于缓存提前创建Bean对象的lambda表达式。

解决循环依赖的步骤:

  1. 比如说在创建A类的Bean对象前,会将其放到一个creatingSet中,表示这个类的Bean对象正在创建,然后再创建一个A的普通对象,用这个对象构造一个lambda表达式放到三级缓存中,这个表达式可以提前创建一个AOP代理Bean对象。
  2. 如果在依赖注入时发现依赖的B对象在创建时也依赖了A,而且A还在creatingSet中,就说明发生了循环依赖,那么就会去二级缓存中去找,如果也没有就执行三级缓存中的lambda表达式,提前生成A的代理Bean对象放到二级缓存中,并注入到B对象中,这样B竟可以顺利完成Bean对象的构造。
  3. 然后A的普通对象的依赖注入也能顺利完成,再进行一些初始化操作,最后再将二级缓存中的代理Bean对象(内部引用了普通对象)放到一级缓存中,完成创建。

1. 什么情况下的循环依赖不会自动解决

没有无参构造器,或者带参构造器加了@Autowired注解,Spring会自动调用带参的构造器构造普通对象,并自动注入参数,而如果这个参数对象也反过来依赖了原对象,那么这种循环依赖就没法自动解决了,因为构造器方法无法执行普通对象都没法创建出来,三级缓存中也就不存在相关的lambda表达式了。

2. @Lazy注解解决上述问题

在A的带参构造器方法上加上@Lazy注解,当参数对象B循环依赖了A时,会直接创建一个B的代理对象(非真正的Bean对象),那么A的Bean对象就可以顺利完成。当调用A中B代理对象的方法时,实际会调用Spring容器中的B的Bean对象的方法,这样就解决了上述循环依赖问题

参考:https://www.bilibili.com/video/BV1dP411J7tQ

五、Bean的作用域

  1. singleton(默认):单例作用域;在IOC容器中只会生成一个。通常用于无状态的 Bean
  2. prototype:原型作用域;每次获取都会创建新的实例。通常用于有状态的 Bean
  3. request:请求作用域;每次请求生产一个
  4. session:会话作用域;一个 Http Session 中定义一个 Bean 实例
  5. application:全局作用域。一个Servlet 容器中定义一个 Bean 实例。

注意:后 3 种作用域,只适用于 Spring MVC 框架。
在这里插入图片描述

参考:https://www.zhihu.com/question/485732854/answer/2653375598

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值