之前我们知道了spring ioc的加载过程, 具体如下图. 下面我们就来对照下图, 看看ioc加载的源代码.

下面在用装修类比, 看看个个组件都是怎么工作的.

接下来是源码分析的整体结构图. 对照上面的思路梳理出来的

一、源码分析的入口
通常,我们的入口都是从main方法进入. 这里我们也来定义一个main方法

public class MainStarter {
public static void main(String[] args) {
// 第一步: 通过AnnotationConfigApplicationContext读取一个配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainStarter.class);
context.scan("package name");
Car car = (Car) context.getBean("car");
System.out.println(car.getName());
context.close();
}
}

顺便再来看看还有哪些相关的类

/**
* 这是一个配置类,
* 在配置类里面定义了扫描的包路径com.lxl.www.iocbeanlifecicle
* 这是会将这个包下配置了注解的类扫描到ioc容器里面,成为一个成熟的bean
*/
@Configuration
@ComponentScan(basePackages = {"com.lxl.www.iocbeanlifecicle"})
public class MainConfig {
}

这个类有一个注解@Configuration, 这样这个类会被扫描成bean
还有一个注解@ComponentScan(backPackage = {"com.lxl.www.iocbeanlifecicle"}) 他表示, 请扫描com.lxl.www.iocbeanlifecicle包下所有的类.
com.lxl.www.iocbeanlifecicle 这个包下还有哪些类呢? 我们来看看项目结构

这是这个包下完整的项目结构.
下面会逐渐说明, 每个类的用途
二. 最重要的类BeanFactory
我们知道在将一个class加载为bean的过程中BeanFactory是最最重要的, 那么他是何时被加载的呢?
我们来跟踪一下带有一个参数的构造方法AnnotationConfigApplicationContext

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// 进入构造函数, 首先调用自身的构造方法this();
// 调用自身的构造方法之前, 要先调用父类的构造方法
this();
// retister配置注册类
register(componentClasses);
// ioc容器shua新接口--非常重要
refresh();
}

这就是AnnotationConfigApplicationContext初始化的时候做的三件事
第一件事: this(); //调用自身的无参构造方法. 同时调用父类的构造方法
第二件事: register(componentClasses); // 调用注册器, 这里会加载两个BeanDefinitionReader和BeanDefinitionScanner. 这两位的角色是什么呢? 可以回忆一下之前的框架图
第三件事: refresh(); // 这是ioc容器刷新, 非常重要. 无论是spring boot还是spring mvc都有这个方法. 这个方法包含了整个spring ioc加载的全生命流程. 也是我们要重点学习的方法
下面来看看BeanFactory是何时被加载进来的呢?
在初始化方法的时候调用了自身的无参构造函数, 在调用自身无参构造函数的时候, 同时会调用父类的无参构造函数.
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
......
}
父类是GenericApplicationContext, 其无参构造函数就做了一件事

public GenericApplicationContext() {
// 构造了一个BeanFactory.
// 在调用GenericApplicationContext父类构造函数, 为ApplicationContext spring上下文对象初始化beanFactory
// 为什么初始化的是DefaultListableBeanFactory呢?
// 我们在看BeanFactory接口的时候发现DefaultListableBeanFactory是最底层的实现, 功能是最全的.
// 查看
this.beanFactory = new DefaultListableBeanFactory();
}

初始化DefaultListableBeanFactory.
问题: BeanFactory有很多, 为什么初始化的时候选择DefaultListableBeanFactory呢?
我们来看看DefaultListableBeanFactory的结构. 快捷键option + command + u --> Java Class Diagrams

通过观察, 我们发现, DefaultListableBeanFactory实现了各种各样的BeanFactory接口, 同时还是先了BeanDefinitionRegistry接口.
也就是说, DefaultListableBeanFactory不仅仅有BeanFactory的能力, 同时还有BeanDefinitionRegistry的能力. 它的功能是最全的.
所以, 我们使用的是一个功能非常强大的类Bean工厂类.
AnnotationConfigApplicationContext继承了GenericApplicationContext,
而 GenericApplicationContext 实现了AnnotationConfigRegistry接口.
所以AnnotationConfigApplicationContext有AnnotationConfigRegistry的能力.
三. bean定义读取器AnnotatedBeanDefinitionReader
接着上面, 第一步调用的是this(). 也就是AnnotationConfigApplicationContext的无参构造函数. 在这个无参构造函数里一共做了两件事情

public AnnotationConfigApplicationContext() {
/**
* 创建了一个Bean定义的读取器.
* 完成了spring内部BeanDefinition的注册(主要是后置处理器)
* 读取了很多spring自定义的配置(主要是后置处理器). 这些类都是spring 的原始类.
*/
this.reader = new AnnotatedBeanDefinitionReader(this);
/**
* 创建BeanDefinition扫描器
* 可以用来扫描包或者类, 进而转换为bd
*
* Spring默认的扫描包不是这个scanner对象
* 而是自己new的一个ClassPathBeanDefinitionScanner
* Spring在执行工程后置处理器ConfigurationClassPostProcessor时, 去扫描包时会new一个ClassPathBeanDefinitionScanner
*
* 这里的scanner仅仅是为了程序员可以手动调用AnnotationConfigApplicationContext对象的scan方法
* 通过调用context.scan("package name");扫描处理配置类
* 扫描
*/
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

1. 初始化AnnotatedBeanDefinitionReader.
2. 初始化ClassPathBeanDefinitionScanner
我们先来看看AnnotatedBeanDefinitionReader

在这里的描述中, 我们知道BeanDefinitionReader是要去扫描配置或者注解, 如果理解为销售的话, 就是扫描楼盘. 这里面就有我们的潜在用户. 也就是我们需要将其转换为bean的对象.
那么初始化的时候,AnnotatedBeanDefinitionReader做了什么呢?
重点看这句

public AnnotatedBeanDefinitionReader(BeanDefinitio

本文深入分析了Spring5中IOC容器的加载流程,从main方法入口开始,详细介绍了BeanFactory如何加载、AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的作用,以及refresh()方法在bean实例化过程中的关键步骤,包括invokeBeanFactoryPostProcessors和finishBeanFactoryInitialization。
最低0.47元/天 解锁文章
791

被折叠的 条评论
为什么被折叠?



