Java 面试扩展:spring bean生命周期、三级缓存

实例化 Bean 对象。设置 Bean 属性。如果我们通过各种 Aware 接口声明了依赖关系,则会注入 Bean 对容器基础设施层面的依赖。具体包括 BeanNameAware、BeanFactoryAware 和 ApplicationContextAware,分别会注入 Bean ID、Bean Factory 或者 ApplicationContext。调用 BeanPostProcessor 的前置初始化方法 postProcessBeforeInitialization。如果实现了 InitializingBean 接口,则会调用 afterPropertiesSet 方法。调用 Bean 自身定义的 init 方法。调用 BeanPostProcessor 的后置初始化方法 postProcessAfterInitialization。创建过程完毕。

 

Spring Bean 有五个作用域:

Singleton,这是 Spring 的默认作用域,也就是为每个 IOC 容器创建唯一的一个 Bean 实例。

Prototype,针对每个 getBean 请求,容器都会单独创建一个 Bean 实例。

如果是 Web 容器,则支持另外三种作用域:

Request,为每个 HTTP 请求创建单独的 Bean 实例。,为每个请求创建一个bean实例。

Session,很显然 Bean 实例的作用域是 Session 范围。为每个会话创建一个bean实例。

GlobalSession,用于 Portlet 容器,因为每个 Portlet 有单独的 Session,GlobalSession 提供一个全局性的 HTTP Session。

他们是什么时候创建的:
1一个单例的bean,而且lazy-init属性为false(默认),在Application Context创建的时候构造
2一个单例的bean,lazy-init属性设置为true,那么,它在第一次需要的时候被构造.
3 其他scope的bean,都是在第一次需要使用的时候创建

他们是什么时候销毁的:
1 单例的bean始终 存在与application context中, 只有当 application 终结的时候,才会销毁
2 和其他scope相比,Spring并没有管理prototype实例完整的生命周期,在实例化,配置,组装对象交给应用后,spring不再管理.只要bean本身不持有对另一个资源(如数据库连接或会话对象)的引用,只要删除了对该对象的所有引用或对象超出范围,就会立即收集垃圾.
3 Request: 每次客户端请求都会创建一个新的bean实例,一旦这个请求结束,实例就会离开scope,被垃圾回收.
4 Session: 如果用户结束了他的会话,那么这个bean实例会被GC.

 

Spring AOP:

Aspect,通常叫作方面,

Join Point,它是 Aspect 可以切入的特定点,在 Spring 里面只有方法可以作为 Join Point。

Advice,它定义了切面中能够采取的动作。

Pointcut 是解决了切面编程中的 Where 问题,Advice 则是明确了切面编程中的 What,也就是做什么

 

在了解Spring解决循环依赖问题之前,需要了解注入属性的方式、Spring中的三级缓存、bean的创建过程。

关于注入属性的方式

上面说到创建A时B属性的注入,如果是构造方法去注入,那循环依赖问题没有方法解决,如果是Set方法注入,那就可以使用三级缓存来解决,Spring中就是这样的解决的。

Bean的大致创建过程

实例化—设置属性—初始化

Spring中的三个缓存是什么

一级缓存:singletonObjects,存放初始化后的单例对象
二级缓存:earlySingletonObjects,存放实例化,未完成初始化的单例对象(未完成属性注入的对象)
三级缓存:singletonFactories,存放ObjectFactory对象
都是Map集合
单例对象先实例化存在于singletonFactories中,后存在于earlySingletonObjects中,最后初始化完成后放入singletonObjects中**。

Spring是如何解决循环依赖问题的?

上面说到Spring是使用三级缓存(Map)解决的循环依赖问题,那具体是怎么做的,看下面的步骤。
假设A依赖B,B依赖A,Spring创建A实例的过程如下:

1、A依次执行doGetBean方法、依次查询三个缓存是否存在该bean、没有就createBean,实例化完成(早期引用,未完成属性装配),放入三级缓存中,接着执行populateBean方法装配属性,但是发现装配的属性是B对象,走下面步骤。

2、创建B实例,依次执行doGetBean、查询三个缓存、createBean创建实例,接着执行populateBean发现属性中需要A对象。

3、再次调用doGetBean创建A实例,查询三个缓存,在三级缓存singletonFactories得到了A的早期引用(在第一步的时候创建出来了),将它放到二级缓存并移除3级缓存并返回,B完成属性装配,一个完整的对象放到一级缓存singletonObjects中。

4、B完成之后就回到A了,A得到完整的B,肯定也完成全部初始化,也存入一级缓存中。
解决了循环依赖问题。

为什么不使用二级缓存?

如果仅仅是解决循环依赖问题,二级缓存也可以,但是如果注入的对象实现了AOP,那么注入到其他bean的时候,不是最终的代理对象,而是原始的。通过三级缓存的ObjectFactory才能实现类最终的代理对象。

一级缓存能不能解决循环依赖问题?

可以解决,但是因为初始化完成和未初始化完成的都放在这个map中,拿到的可能是没有完成初始化的,属性都是空的,直接空指针异常。

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值