Spring底层原理探究

2 篇文章 0 订阅

Spring底层原理

前言

  离上一篇文章记录差不多过了半年左右的时间吧,这段时间很多面试准备,知识点的学习基本被我转移到了书面笔记上,秋招仍在继续,你我怎能懈怠!特捡起上半年的老本,继续自己的CSDN博客之途。

  看过我之前写的文章的小伙伴肯定都有一个感受,啊写的好基础呀!没错是的,之前无论是技术知识的积累还是知识层面的广度和深度,都近似一个小白在写杂谈…

  这段很长时间的离开希望能让以后我的分享提升一个档次,至于为什么要坚持写博客?

  1. 互联网时代软件行业技术开源精神的普及,分享技术是一件有助于自己,更帮助其他人从而达到共赢的很棒的事情。
  2. 帮助自己巩固记忆知识点,好记性不如烂笔头,同时费曼学习法教会我们如果能把别人讲懂,说明我们已经熟练掌握了该知识点。
  3. 自己喜欢阅读和写作这两件事,将自己想要表达的观点和看法写在文章里,是一件很有成就感的事情。不管有没有人阅读,负面评论什么的,自己从中可以获取到乐趣。
目标

  由于仍处于秋招白热化期间,我可能有时沉浸在自己面经的复习中,但我计划至少两天写一篇原创文章。希望可以完成!

正文

  我们知道Spring是Jave EE里面划时代的轻量级框架设计,代替了之前重量级的EJB技术(Enterprise JavaBean),可以解决对象依赖问题,降低耦合性,同时提供声明式事务管理功能。
  Spring两个核心功能为 控制反转(IoC)和提供面向切面编程(AOP)。

 今天我们就来了解下Spring的核心IoC容器,它是怎么导入bean并进行实例化对象操作的?

导入Bean

第一个问题我们首先要知道我们通过哪几种方式可以为IoC容器导入bean?

  1. xml方式
    xml作为最基本的配置方式,其实是比较重量级比较繁琐的一种方式,我们在xml文件中声明一个个的 <bean标签,指定bean的id和要导入的全类名。

  2. Java类配置
    使用Java类定义Spring配置。使用@Configuration注解需要作为配置的类。(表示该类将定义Bean)

    @Configuration
    public class ApplicationContextConfig{
    	@Bean
    	public String message() {
    		return "hello"}
    }
    
    
    
  3. 开发中我们用的最多的是组件注解,@Component, 常见的有@Controller, @Repository, @Service。

BeanDefinition

 上述几种方式可以为我们的IoC容器导入bean,那么问题来了,我们导入IoC容器中的bean 被 Spring 识别成什么了呢,怎么进行实例化的呢?

 我们知道我们可以通过容器提供的 getBean( )方法获取到对应id的bean对象,但是在 getBean() 之前做了哪些操作呢?

 我们就要了解一下BeanDefinition这个对象了,我们被注解标记的类被JVM类加载器加载到内存中形成对应的BeanDedifinitionbean定义对象,存储在一个map集合(BeanDefinitionMap)中。如下图:
在这里插入图片描述


 相信很多朋友都没听说过Spring里面的这个bean定义对象,没关系,我们学习新知识都是从一开始什么都不知道到了解概念再到熟练,精通。

 单单BeanDefinition可不能直接实例化,里面需要涉及到一个接口,BeanFactoryPostProcessor,我们可以把它理解为在实例化对象之前可以修改我们的bean定义对象即BeanDedinition对象,这个接口中的一个实现类BeanDefinitionRegistryPostProcessor可以完成类似“设计小组”的功能,对字节码进行解析,这个接口的另外一个实现类可以实现类似“审核小组”的功能,用于修改 bean定义对象。

 听到这里可能很多小伙伴已经云里雾里了,“这什么乱七八糟的,你的文笔就这,就这??”

 别急,我可以拿人给大家打个不恰当的比喻,一个已经实例化的bean对象可以看成是一个已经18岁的成年人了。

 这个BeanDefinition我们可以把它看作我们人最初的形态——受精卵,我们以后长成人的基因已经在这里确定了,那上面所说的BeanFactoryPostProcessor可以对bean定义对象作修改的嘛,没事啊,我们受精卵也可以作修改啊,不是有个“人类基因组计划”嘛,基因工程可以改变bean定义对象啊。之后getBean() 获取到实例对象,此时我们知道还有一个BeanPostProcessor接口(注意这个和上面的接口不一样!),这个就相当于我们现在流行的整容变性啊,之后真正成长成我们想要的样子了。(说多了都是泪啊!)

这就是导入到IoC容器后bean被Spring识别的过程。

实例化Bean

 上面我们知道经过bean的定义解析过程,我们需要调用 getBean() 来实例化对象了,IoC容器是怎么一步步实例化对象的呢?

 我们可以通过源码打断点跟随着getBean()的调用栈一步步地分析~
总体调用栈为如下所述:

getBean() ----> doGetBean() ----->getSingleton() ----->createBean() ------>doCreateBean(),到这里我们停一下,这一步就是真正的创建我们的bean实例对象的过程。
 通过查看源码我们知道它调用createBeanInstance()方法通过反射来创建对象(早期对象),注意此时对象只是一个空壳对象,并不能直接构成我们最后的单例对象(Spring的IoC容器默认是单例的)。这里我们不得不简单科普一下Spring中的三级缓存了,不然咱们无法继续下去。

Spring三级缓存

一级缓存:就是大名鼎鼎的单例缓存池,用于保存所有的单实例 bean。

	singletonObjects = new ConCurrentHashMap<>();	

终于看到ConCurrentHashMap的实际应用场景了hh。
三级缓存:就是在createBeanInstance() 方法后创建的早期对象存放的地方。

	singletonFactories = new HashMap<>();	

我们总结:

  1. 所有的对象创建的过程中,都会去把自己的早期对象暴露到三级缓存中去。
  2. 针对代理对象的时候,从三级缓存到二级缓存会经过一次代理,创建完毕后,就会删除2级缓存,把单例对象存储到单例缓存池中。

  紧接着上述创建早期对象,我们通过addSingletonFactory() 将早期对象暴露到三级缓存中,再调用populateBean() 设置早期对象的属性,如果发现其中依赖其他bean,则对其他bean执行同样的步骤,如果出现循环依赖的问题(即A依赖B,B依赖A),Spring的三级缓存可帮我们解决这个问题,我们在开发中并没有注意到这个问题。

  以上就是今天总结分享的Spring导入bean的三种方式以及导入后bean的生命周期,由于博主也是没有工作经验的校招程序员,所以有很多地方说的不是很清楚甚至会引起误解,大家可以提出来我一定会悉心听取意见。

  如果喜欢我的文章可以点点赞关注我,我也计划至少两天一篇原创文章。如果你能看到这里说明你肯定是一名热爱学习的人,真诚希望我们能为了自己的目标或者梦想不断努力,不断学习,希望大家都能得到自己想要的! 我是 promise,一名喜欢思考人生,喜欢讲大道理的程序员。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值