Spring源码系列之Spring整体简介(一)

Spring框架作为我们java程序员来说都是再熟悉不过的,我们平常所使用的SpringBoot亦或者是SpringCloud都跟我们的Spring息息相关。但是当我们在使用SpringBoot或者SpringCloud的时候,有没有想过他们的核心到底是什么?其实很简单,就是我们的IOC和AOP,也就是我们都知道的Spring的两大核心点。

IOC和AOP

IOC就是我们所谓的控制反转,AOP也就是我们说的面向切面。我相信大家肯定都用过@Service@Component注解。那么被这两个注解所托管的容器就叫IOC容器,而AOP呢其实就是在我们容器进行实例化对象后对我们的Bean进行一个CGLIB或者JDK的一个动态代理,通过解析我们的表达式来选择我们需要去处理我们需要进行动态代理的类。这两个东西最核心的其实还是IOC,而AOP其实是IOC的一个分支节点,他不管理我们的容器,但是会拓展我们的IOC容器进行一个Bean的处理。

IOC核心组件

什么是IOC的高级用法?我来举个例子,比如我们SpringBoot的自动装配,有很多小伙伴无论是面试者还是面试官,都喜欢问SpringBoot自动装配的原理这种问题。其实在我看来SpringBoot自动装配其实就是Ioc的高级玩法之一。那么既然是IOC的高级玩法,它的底层还是我们的IOC容器为核心。而我们的IOC容器也有他自己所依赖的核心。

1.BeanDefinition

BeanDefinition大家可以理解为一个图纸,我们中文叫他Bean定义,实际上Definition也确实是定义的意思。那么他就是将我们的@Conponent@Configuration@Scope等等的注解解析成一个个的组装零件,最后交由我们的Bean工厂去进行处理。那我们想一下。我们的Spring是不是有两种用法,第一种:xml
第二种:注解。所以这两个东西需要不同的东西去对他们解析处理,处理成统一的BeanDefinition。那么他们到底是怎么去处理不同的配置方式呢?

2.BeanDefinition三大组件
  1. BeanDefinitionReader
  2. BeanDefinitionScanner
  3. BeanDefinitionRegistry

我们先来看一下BeanDefinitionReader在Spring源码中的情况。我们以注解的方式来看一下。首先我们先写一个方法

public class MainClass {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
		context.getBean("a");
		System.out.println(context.getBean("a"));
	}
}

然后我们跟进去AnnotationConfigApplication的构造方法。

public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

在构造方法里,会帮我们注册两个,第一个是BeanDefinitionReader,第二个就是BeanDefinitionScanner。我们只需要看Reader就好。那么Reader其实在我们加载配置类的时候会调用一次Register方法去读取我们的配置类。
在这里插入图片描述
所以我们可以得出一个结论:BeanDefinitionReader是用来读取配置类的。那么BeanDefinitionScanner又是干啥的呢?
在这里插入图片描述
直接定位到它的scan方法,它就是用来扫描我们的包的,并且将选定的类加入候选名单里
在这里插入图片描述
再拥有了读取和扫描之后,我们需要一个地方来存储这些东西对吧,那么Spring是怎么存储的呢?
Spring实际上是将BeanDefinition存储到了BeanDefinitionMap里。没错这确实就是一个Map的KV缓存池,里面缓存了我们各种各样的BeanDefinition,而完成这一步的就是由BeanDefinitionRegistry做到的。
但是我们刚刚好像没有看到说哪里有实例化BeanDefinitionRegistry,我们只看到了BeanDefinitionReader和BeanDefinitionScanner。我们先找到BeanDefinitionRegistry这个类
在这里插入图片描述
可以看到它就是一个接口,而对应的接口肯定就有实现类对吧。我们看一下他了UML类图
在这里插入图片描述
从中我们可以看到其实我们的ApplicationContext已经实现了BeanDefinitionRegistry,所以我们的ApplicationContext就已经拥有了注册BeanDefinition到BeanDefinitionMap中的一个能力。
那么现在万事俱备只欠东风了,我们有了图纸那么就要对着图纸进行生产加工,而生产加工就是交由BeanFactory去完成的。
总体来说BeanFactory主要做这4个步骤,也就是所谓坊间流传的Bean的生命周期。

  1. 实例化
  2. 填充属性
  3. 初始化
  4. put到单例缓存池
    在实例化这一步,我们的Bean被刚刚创建出来,里面还没有对我们的@Autowired@Resource等注解进行一个属性填充。在我看来实例化只有一种方式就是反射,无论你是用过newInstance还是通过构造器,都避免不了反射这一环节,所以实例化最终是通过反射创建出来的一个空壳对象。
    而填充属性就是通过反射对我们的@Autowired等注解进行一个填充,不过这里会有一个非常著名的一个问题:循环依赖。所谓循环依赖就是假设A依赖B,B依赖A。那么我在填充属性的时候就会因为他们之间的互相依赖而导致的死循环。当然解决循环依赖不是我们这一章需要讲的,在之后的文章里我会讲到循环依赖是怎么样被Spring解决的。
    初始化就是比如我们的initMethod或者destroy等等都是初始化做的。那么当这些都做完了只有就会放到我们的单例缓存池里,方便我们进行调用。所以我们也可以理解为Spring是一个单例聚集地。
    单例缓存池

Spring拓展点

当然我们Spring肯定有非常多的可供拓展的地方,虽然我们平时开发中可能很少会用到,但是它也是和Spring密不可分的一个东西。它被大量的运用到了Spring内部一些处理类当中。我以我自己写的框架举例
在这里插入图片描述
我自己写的框架当中的AnnotationRegistry其实就是对我自己内部的注解进行注册,通过Registry来注册一个注解,最后调用handler方法来让实现了AnnotationRegistry的类自己去处理class或者实例对象。这样做的好处就是可以方便我们日后拓展功能并且和我们的业务进行一个解耦操作。
在这里插入图片描述
在这里也可以看到我注册了一个IOC的注解并且通过该注解去处理我们的类。而我们的Spring也正是这么玩的。在我们Spring中有两个核心拓展接口

  1. beanFactoryPostProcessor
  2. BeanPostProcessor
    第一个是用来在还没有实例化的时候还是一个BeanDefinition的时候对我们的类进行一个处理
    而第二个就是对我们实例化后的对象进行一个处理。而这两个又有许多Spring内部类去实现。
    在这里插入图片描述

在这里插入图片描述
这些东西都会在我后面的文章一一介绍到。
好了今天的文章就到这里,喜欢的话记得评论转发点赞收藏哦~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值