.spring5源码 -- ioc加载的整体流程

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

之前我们知道了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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值