Spring Bean 的生命周期以及三级缓存

一、Spring Bean生命周期简介

Spring Bean的生命周期是从Bean实例化之后,即通过反射创建出对象,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为3个阶段:

  • Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否是延迟加载的,是否是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;
  • Bean的初始化阶段:Bean创建后还仅仅是一个半成品,还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义的初始化init方法等,该阶段是Spring最具有技术含量和复杂度的阶段,Aop增强功能、Spring的注解功能、Bean的循环引用问题等都是在这个阶段体现的;
  • Bean的完成阶段:经过初始化阶段,Bean就成为一个完整的Spring Bean,被存储到单例池SingletonObjects中,即完成了一个完成的Spring Bean周期。

其中Bean的初始化阶段是Spring Bean的生命周期中比较复杂和重要的部分,涉及如下几个过程:

  • Bean实例的属性填充;

            分为如下几种情况:

  1. 注入普通属性,String、int或者存储基本类型的集合时,直接通过set方法的反射设置属性值;
  2. 注入单向对象引用属性时,从容器中getBean获取后通过set方法的反射设置属性,如果容器中没有(由于容器管理对象不分先后,所以需要的对象属性可能还没有实例化),则先暂停初始化,去创建被注入对象Bean实例(完成整个生命周期)后,在反过来开始初始化,将需要设置的对象属性赋值;
  3. 注入双向对象引用属性,涉及循环引用(循环依赖)问题,比较复杂,这里不做描述,下面的  Spring Bean生命周期中的循环依赖问题 会作详细介绍(通过三级缓存解决循环引用)
  • Aware接口的属性注入;
  • BeanPostProcessor的Before()方法回调;
  • InitializingBean接口的初始化方法回调;
  • 自定义初始化init方法的回调;
  • BeanPostProcessor的after()方法回调;

二、Spring Bean生命周期中的循环依赖问题

     1.循环依赖

在Java中,循环依赖通常发生在依赖注入的场景中,指的是两个或多个bean相互依赖对方,形成了一个循环,导致容器无法正确解析和初始化这些bean。如下代码:

@Component
public class BeanA {
    @Autowired
    private BeanB beanB;
    // ...
}

@Component
public class BeanB {
    @Autowired
    private BeanA beanA;
    // ...
}

在这个例子中,BeanA 依赖 BeanB,而 BeanB 又依赖 BeanA,这就形成了一个循环依赖。

2.解决循环依赖

Spring框架通过三级缓存帮助解决循环依赖,如下是关于三级缓存的代码:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  
    //最终存储单例Bean成品的容器,即实例化和初始化完毕的Bean,称之为“一级缓存”
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    //早期Bean单例池,缓存半成品Bean,且当前对象已经被其他对象引用了,称之为“二级缓存”
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
    //单例Bean的工厂池,缓存半成品Bean,对象未被引用,使用时在通过工厂创建Bean,称之为“三级缓存”
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
   
    }

解决循环依赖的过程:

public class UserServiceImpl implements UserService {
    private UserDaoImpl userDao;

    public void setUserDao(UserDaoImpl userDao) {
        this.userDao = userDao;
    }
}


public class UserDaoImpl implements UserDao {

    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }
}

1.userService实例化对象,但尚未初始化,未被其他对象引用,将userService存储到三级缓存

2.userService属性注入,需要userDao,从上述缓存中获取,此时缓存中没有userDao

3.userDao实例化对象,但尚未初始化,未被其他对象引用,将userDao存储到三级缓存

4.userDao属性注入,需要userService,从三级缓存中可以获取userService,此时userService被userDao引用,将userService从三级缓存移入到二级缓存(三级缓存已经没有userService)

5.userDao执行其他生命周期过程,最终初始化为完整的Bean对象,将userDao存储到一级缓存,并将userDao从二三级缓存删除(其实是从三级缓存删除,二级缓存并没有)

6.回到userService属性注入,此时UserDao在一级缓存中已经存在,直接注入UserDao

7.userService执行其他生命周期过程,最终初始化为完整的Bean对象,将userService存储到一级缓存,并将userService从二三级缓存删除(其实是从二级缓存删除,三级缓存并没有)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值