跨境互联网 跨境互联网
首页
  • AI 工具

    • 绘图提示词工具 (opens new window)
    • ChatGPT 指令 (opens new window)
  • ChatGPT

    • ChatGP T介绍
    • ChatGPT API 中文开发手册
    • ChatGPT 中文调教指南
    • ChatGPT 开源项目
  • Midjourney

    • Midjourney 文档
  • Stable Diffusion

    • Stable Diffusion 文档
  • 其他

    • AIGC 热门文章
    • 账号合租 (opens new window)
    • 有趣的网站
  • Vue

    • Vue3前置
  • JAVA基础

    • Stream
    • Git
    • Maven
    • 常用第三方类库
    • 性能调优工具
    • UML系统建模
    • 领域驱动设计
    • 敏捷开发
    • Java 测试
    • 代码规范及工具
    • Groovy 编程
  • 并发编程&多线程

    • 并发编程
    • 高性能队列 Disruptor
    • 多线程并发在电商系统下的应用
  • 其他

    • 面试题
  • 消息中间中间件

    • Kafka
    • RabbitMQ
    • RocketMQ
  • 任务调度

    • Quartz
    • XXL-Job
    • Elastic-Job
  • 源码解析

    • Mybatis 高级使用
    • Mybatis 源码剖析
    • Mybatis-Plus
    • Spring Data JPA
    • Spring 高级使用
    • Spring 源码剖析
    • SpringBoot 高级使用
    • SpringBoot 源码剖析
    • Jdk 解析
    • Tomcat 架构设计&源码剖析
    • Tomcat Web应用服务器
    • Zookeeper 高级
    • Netty
  • 微服务框架

    • 分布式原理
    • 分布式集群架构场景化解决方案
    • Dubbo 高级使用
    • Dubbo 核心源码剖析
    • Spring Cloud Gateway
    • Nacos 实战应用
    • Sentinel 实战应用
    • Seata 分布式事务
  • 数据结构和算法的深入应用
  • 存储

    • 图和Neo4j
    • MongoDB
    • TiDB
    • MySQL 优化
    • MySQL 平滑扩容实战
    • MySQL 海量数据存储与优化
    • Elasticsearch
  • 缓存

    • Redis
    • Aerospike
    • Guava Cache
    • Tair
  • 文件存储

    • 阿里云 OSS 云存储
    • FastDF 文件存储
  • 基础

    • Linux 使用
    • Nginx 使用与配置
    • OpenResty 使用
    • LVS+Keepalived 高可用部署
    • Jekins
  • 容器技术

    • Docker
    • K8S
    • K8S
  • 01.全链路(APM)
  • 02.电商终极搜索解决方案
  • 03.电商亿级数据库设计
  • 04.大屏实时计算
  • 05.分库分表的深入实战
  • 06.多维系统下单点登录
  • 07.多服务之间分布式事务
  • 08.业务幂等性技术架构体系
  • 09.高并发下的12306优化
  • 10.每秒100W请求的秒杀架构体系
  • 11.集中化日志管理平台的应用
  • 12.数据中台配置中心
  • 13.每天千万级订单的生成背后痛点及技术突破
  • 14.红包雨的架构设计及源码实现
  • 人工智能

    • Python 笔记
    • Python 工具库
    • 人工智能(AI) 笔记
    • 人工智能(AI) 项目笔记
  • 大数据

    • Flink流处理框架
  • 加密区

    • 机器学习(ML) (opens new window)
    • 深度学习(DL) (opens new window)
    • 自然语言处理(NLP) (opens new window)
AI 导航 (opens new window)

Revin

首页
  • AI 工具

    • 绘图提示词工具 (opens new window)
    • ChatGPT 指令 (opens new window)
  • ChatGPT

    • ChatGP T介绍
    • ChatGPT API 中文开发手册
    • ChatGPT 中文调教指南
    • ChatGPT 开源项目
  • Midjourney

    • Midjourney 文档
  • Stable Diffusion

    • Stable Diffusion 文档
  • 其他

    • AIGC 热门文章
    • 账号合租 (opens new window)
    • 有趣的网站
  • Vue

    • Vue3前置
  • JAVA基础

    • Stream
    • Git
    • Maven
    • 常用第三方类库
    • 性能调优工具
    • UML系统建模
    • 领域驱动设计
    • 敏捷开发
    • Java 测试
    • 代码规范及工具
    • Groovy 编程
  • 并发编程&多线程

    • 并发编程
    • 高性能队列 Disruptor
    • 多线程并发在电商系统下的应用
  • 其他

    • 面试题
  • 消息中间中间件

    • Kafka
    • RabbitMQ
    • RocketMQ
  • 任务调度

    • Quartz
    • XXL-Job
    • Elastic-Job
  • 源码解析

    • Mybatis 高级使用
    • Mybatis 源码剖析
    • Mybatis-Plus
    • Spring Data JPA
    • Spring 高级使用
    • Spring 源码剖析
    • SpringBoot 高级使用
    • SpringBoot 源码剖析
    • Jdk 解析
    • Tomcat 架构设计&源码剖析
    • Tomcat Web应用服务器
    • Zookeeper 高级
    • Netty
  • 微服务框架

    • 分布式原理
    • 分布式集群架构场景化解决方案
    • Dubbo 高级使用
    • Dubbo 核心源码剖析
    • Spring Cloud Gateway
    • Nacos 实战应用
    • Sentinel 实战应用
    • Seata 分布式事务
  • 数据结构和算法的深入应用
  • 存储

    • 图和Neo4j
    • MongoDB
    • TiDB
    • MySQL 优化
    • MySQL 平滑扩容实战
    • MySQL 海量数据存储与优化
    • Elasticsearch
  • 缓存

    • Redis
    • Aerospike
    • Guava Cache
    • Tair
  • 文件存储

    • 阿里云 OSS 云存储
    • FastDF 文件存储
  • 基础

    • Linux 使用
    • Nginx 使用与配置
    • OpenResty 使用
    • LVS+Keepalived 高可用部署
    • Jekins
  • 容器技术

    • Docker
    • K8S
    • K8S
  • 01.全链路(APM)
  • 02.电商终极搜索解决方案
  • 03.电商亿级数据库设计
  • 04.大屏实时计算
  • 05.分库分表的深入实战
  • 06.多维系统下单点登录
  • 07.多服务之间分布式事务
  • 08.业务幂等性技术架构体系
  • 09.高并发下的12306优化
  • 10.每秒100W请求的秒杀架构体系
  • 11.集中化日志管理平台的应用
  • 12.数据中台配置中心
  • 13.每天千万级订单的生成背后痛点及技术突破
  • 14.红包雨的架构设计及源码实现
  • 人工智能

    • Python 笔记
    • Python 工具库
    • 人工智能(AI) 笔记
    • 人工智能(AI) 项目笔记
  • 大数据

    • Flink流处理框架
  • 加密区

    • 机器学习(ML) (opens new window)
    • 深度学习(DL) (opens new window)
    • 自然语言处理(NLP) (opens new window)
AI 导航 (opens new window)
  • Spring Data JPA
  • MyBatis

  • Spring

  • SpringBoot

    • SpringBoot高级使用

    • SpringBoot源码剖析

      • 介绍
      • 源码剖析-依赖管理
      • 源码剖析-自动配置
      • 源码剖析-Run方法执行流程
        • SpringApplication() 构造方法
          • deduceWebApplicationType();
          • setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
          • setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
          • 总结
        • run(args)
          • 第一步:获取并启动监听器
          • 第二步:构造应用上下文环境
          • ConfigurableEnvironment environment = getOrCreateEnvironment();
          • configureEnvironment(environment, applicationArguments.getSourceArgs());
          • listeners.environmentPrepared(environment);
          • 第三步:初始化应用上下文
          • 第四步:刷新应用上下文前的准备阶段
          • prepareContext()方法
          • getBeanDefinitionRegistry()
          • createBeanDefinitionLoader()
          • loader.load();
          • 第五步:刷新应用上下文(IOC容器的初始化过程)
          • obtainFreshBeanFactory();
          • prepareBeanFactory(beanFactory);
          • 四、postProcessBeanFactory(beanFactory);
          • 五、invokeBeanFactoryPostProcessors(beanFactory);(重点)
          • findCandidateComponents(basePackage);
          • registerBeanDefinition(definitionHolder, this.registry);
          • @Import注解的解析过程
          • 第六步:刷新应用上下文后的扩展接口
      • 源码剖析-自定义Starter
      • 源码剖析-内嵌Tomcat
      • 源码剖析-自动配置SpringMVC
    • 资料
  • Jdk

  • Tomcat

  • Netty

  • 若依

  • Traefik

  • Openresty

  • 开源框架
  • SpringBoot
  • SpringBoot源码剖析
Revin
2023-06-15
目录

源码剖析-Run方法执行流程

SpringBoot项目的mian函数

@SpringBootApplication //来标注一个主程序类,说明这是一个Spring Boot应用
public class MyTestMVCApplication {


    public static void main(String[] args) {
        SpringApplication.run(MyTestMVCApplication.class, args);
    }
}
1
2
3
4
5
6
7
8

点进run方法

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        // 调用重载方法
        return run(new Class<?>[] { primarySource }, args);
    }




public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        // 两件事:1.初始化SpringApplication  2.执行run方法
        return new SpringApplication(primarySources).run(args);
    }


1
2
3
4
5
6
7
8
9
10
11
12
13
14

# SpringApplication() 构造方法

继续查看源码, SpringApplication 实例化过程,首先是进入带参数的构造方法,最终回来到两个参数的构造方法。

    public SpringApplication(Class<?>... primarySources) {
         this(null, primarySources);
    }


    @SuppressWarnings({"unchecked", "rawtypes"})
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //设置资源加载器为null
        this.resourceLoader = resourceLoader;


        //断言加载资源类不能为null
        Assert.notNull(primarySources, "PrimarySources must not be null");


        //将primarySources数组转换为List,最后放到LinkedHashSet集合中
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));


        //【1.1 推断应用类型,后面会根据类型初始化对应的环境。常用的一般都是servlet环境 】
        this.webApplicationType = WebApplicationType.deduceFromClasspath();


        //【1.2 初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer 】
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));


        //【1.3 初始化classpath下所有已配置的 ApplicationListener 】
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));


        //【1.4 根据调用栈,推断出 main 方法的类名 】
        this.mainApplicationClass = deduceMainApplicationClass();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# deduceWebApplicationType();

    //常量值
    private static final String[] WEB_ENVIRONMENT_CLASSES = {"javax.servlet.Servlet",
               "org.springframework.web.context.ConfigurableWebApplicationContext"};


    private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
         + "web.reactive.DispatcherHandler";


    private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
         + "web.servlet.DispatcherServlet";


    private static final String JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig";


    /**
     * 判断 应用的类型
     * NONE: 应用程序不是web应用,也不应该用web服务器去启动
     * SERVLET: 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
     * REACTIVE: 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。
     * @return
    */
    private WebApplicationType deduceWebApplicationType() {
         //classpath下必须存在org.springframework.web.reactive.DispatcherHandler
         if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
                  && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
                  && !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
         return WebApplicationType.REACTIVE;
      }
        for (String className : WEB_ENVIRONMENT_CLASSES) {
             if (!ClassUtils.isPresent(className, null)) {
                 return WebApplicationType.NONE;
         }
      }
    //classpath环境下存在javax.servlet.Servlet或者org.springframework.web.context.ConfigurableWebApplicationContext
          return WebApplicationType.SERVLET;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

返回类型是WebApplicationType的枚举类型, WebApplicationType 有三个枚举,三个枚举的解释如其中注释

具体的判断逻辑如下:

WebApplicationType.REACTIVE classpath下存在org.springframework.web.reactive.DispatcherHandler WebApplicationType.SERVLET classpath下存在javax.servlet.Servlet或者org.springframework.web.context.ConfigurableWebApplicationContext WebApplicationType.NONE 不满足以上条件。

# setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[]{});
}


/**
 * 通过指定的classloader 从META-INF/spring.factories获取指定的Spring的工厂实例
 * @param type
 * @param parameterTypes
 * @param args
 * @param <T>
 * @return
 */
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
                                                      Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    //通过指定的classLoader从 META-INF/spring.factories 的资源文件中,
    //读取 key 为 type.getName() 的 value
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //创建Spring工厂实例
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    //对Spring工厂实例排序(org.springframework.core.annotation.Order注解指定的顺序)
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

看看 getSpringFactoriesInstances 都干了什么,看源码,有一个方法很重要 loadFactoryNames() 这个方法很重要,这个方法是spring-core中提供的从META-INF/spring.factories中获取指定的类(key)的同一入口方法。

在这里,获取的是key为 org.springframework.context.ApplicationContextInitializer 的类。

debug看看都获取到了哪些

image-20201120173948839

上面说了,是从classpath下 META-INF/spring.factories中获取,我们验证一下:

image-20201120174237303

image-20201120174324947

发现在上图所示的两个工程中找到了debug中看到的结果。

ApplicationContextInitializer 是Spring框架的类, 这个类的主要目的就是在 ConfigurableApplicationContext 调用refresh()方法之前,回调这个类的initialize方法。

通过 ConfigurableApplicationContext 的实例获取容器的环境Environment,从而实现对配置文件的修改完善等工作。

# setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

初始化classpath下 META-INF/spring.factories中已配置的 ApplicationListener。

ApplicationListener 的加载过程和上面的 ApplicationContextInitializer 类的加载过程是一样的。不多说了,至于 ApplicationListener 是spring的事件监听器,典型的观察者模式,通过 ApplicationEvent 类和 ApplicationListener 接口,可以实现对spring容器全生命周期的监听,当然也可以自定义监听事件

# 总结

关于 SpringApplication 类的构造过程,到这里我们就梳理完了。纵观 SpringApplication 类的实例化过程,我们可以看到,合理的利用该类,我们能在spring容器创建之前做一些预备工作,和定制化的需求。

比如,自定义SpringBoot的Banner,比如自定义事件监听器,再比如在容器refresh之前通过自定义 ApplicationContextInitializer 修改配置一些配置或者获取指定的bean都是可以的

# run(args)

上一小节我们查看了SpringApplication 类的实例化过程,这一小节总结SpringBoot启动流程最重要的部分run方法。通过run方法梳理出SpringBoot启动的流程,

经过深入分析后,大家会发现SpringBoot也就是给Spring包了一层皮,事先替我们准备好Spring所需要的环境及一些基础

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 *
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 *
 * 运行spring应用,并刷新一个新的 ApplicationContext(Spring的上下文)
 * ConfigurableApplicationContext 是 ApplicationContext 接口的子接口。在 ApplicationContext
 * 基础上增加了配置上下文的工具。 ConfigurableApplicationContext是容器的高级接口
 */
public ConfigurableApplicationContext run(String... args) {
    //记录程序运行时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // ConfigurableApplicationContext Spring 的上下文
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    //从META-INF/spring.factories中获取监听器
    //1、获取并启动监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        //2、构造应用上下文环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        //处理需要忽略的Bean
        configureIgnoreBeanInfo(environment);
        //打印banner
        Banner printedBanner = printBanner(environment);
        ///3、初始化应用上下文
        context = createApplicationContext();
        //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[]{ConfigurableApplicationContext.class}, context);
        //4、刷新应用上下文前的准备阶段
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //5、刷新应用上下文
        refreshContext(context);
        //刷新应用上下文后的扩展接口
        afterRefresh(context, applicationArguments);
        //时间记录停止
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        //发布容器启动完成事件
        listeners.started(context);
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }


    try {
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

在以上的代码中,启动过程中的重要步骤共分为六步

第一步:获取并启动监听器
第二步:构造应用上下文环境
第三步:初始化应用上下文
第四步:刷新应用上下文前的准备阶段
第五步:刷新应用上下文
第六步:刷新应用上下文后的扩展接口
1
2
3
4
5
6

OK,下面SpringBoot的启动流程分析,我们就根据这6大步骤进行详细解读。最总要的是第四,五步。我们会着重的分析。

# 第一步:获取并启动监听器

事件机制在Spring是很重要的一部分内容,通过事件机制我们可以监听Spring容器中正在发生的一些事件,同样也可以自定义监听事件。Spring的事件为Bean和Bean之间的消息传递提供支持。当一个对象处理完某种任务后,通知另外的对象进行某些处理,常用的场景有进行某些操作后发送通知,消息、邮件等情况。

 private SpringApplicationRunListeners getRunListeners(String[] args) {
     Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
     return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
             SpringApplicationRunListener.class, types, this, args));
 }
1
2
3
4
5

在这里面是不是看到一个熟悉的方法:getSpringFactoriesInstances(),可以看下面的注释,前面的小节我们已经详细介绍过该方法是怎么一步步的获取到META-INF/spring.factories中的指定的key的value,获取到以后怎么实例化类的。

/**
 * 通过指定的classloader 从META-INF/spring.factories获取指定的Spring的工厂实例
 * @param type
 * @param parameterTypes
 * @param args
 * @param <T>
 * @return
 */
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
                                                      Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    //通过指定的classLoader从 META-INF/spring.factories 的资源文件中,
    //读取 key 为 type.getName() 的 value
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //创建Spring工厂实例
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    //对Spring工厂实例排序(org.springframework.core.annotation.Order注解指定的顺序)
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

回到run方法,debug这个代码 SpringApplicationRunListeners listeners = getRunListeners(args); 看一下获取的是哪个监听器:

image-20201120181115928

EventPublishingRunListener监听器是Spring容器的启动监听器。

listeners.starting(); 开启了监听事件。

# 第二步:构造应用上下文环境

应用上下文环境包括什么呢?包括计算机的环境,Java环境,Spring的运行环境,Spring项目的配置(在SpringBoot中就是那个熟悉的application.properties/yml)等等。

首先看一下prepareEnvironment()方法。

private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    //创建并配置相应的环境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //根据用户配置,配置 environment系统环境
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 启动相应的监听器,其中一个重要的监听器 ConfigFileApplicationListener 就是加载项目配置文件的监听器。
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertToStandardEnvironmentIfNecessary(environment);
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

看上面的注释,方法中主要完成的工作,首先是创建并按照相应的应用类型配置相应的环境,然后根据用户的配置,配置系统环境,然后启动监听器,并加载系统配置文件。

# ConfigurableEnvironment environment = getOrCreateEnvironment();

看看getOrCreateEnvironment()干了些什么。

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    //如果应用类型是 SERVLET 则实例化 StandardServletEnvironment
    if (this.webApplicationType == WebApplicationType.SERVLET) {
        return new StandardServletEnvironment();
    }
    return new StandardEnvironment();
}
1
2
3
4
5
6
7
8
9
10

通过代码可以看到根据不同的应用类型初始化不同的系统环境实例。前面咱们已经说过应用类型是怎么判断的了,这里就不在赘述了

image-20201120181443744

从上面的继承关系可以看出,StandardServletEnvironment是StandardEnvironment的子类。这两个对象也没什么好讲的,当是web项目的时候,环境上会多一些关于web环境的配置。

# configureEnvironment(environment, applicationArguments.getSourceArgs());

protected void configureEnvironment(ConfigurableEnvironment environment,
                                    String[] args) {
    // 将main 函数的args封装成 SimpleCommandLinePropertySource 加入环境中。
    configurePropertySources(environment, args);
    // 激活相应的配置文件
    configureProfiles(environment, args);
}
1
2
3
4
5
6
7

在执行完方法中的两行代码后,debug的截图如下

image-20201120182830062

如下图所示,我在spring的启动参数中指定了参数:--spring.profiles.active=prod

(就是启动多个实例用的)

image-20201120182908504

在configurePropertySources(environment, args);中将args封装成了SimpleCommandLinePropertySource并加入到了environment中。

configureProfiles(environment, args);根据启动参数激活了相应的配置文件。

# listeners.environmentPrepared(environment);

进入到方法一路跟下去就到了SimpleApplicationEventMulticaster类的multicastEvent()方法。

-SimpleApplicationEventMulticaster


    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(event));
    }
1
2
3
4
5
6

image-20201120183455960

查看getApplicationListeners(event, type)执行结果,发现一个重要的监听器ConfigFileApplicationListener。

先看看这个类的注释

 * {@link EnvironmentPostProcessor} that configures the context environment by loading
 * properties from well known file locations. By default properties will be loaded from
 * 'application.properties' and/or 'application.yml' files in the following locations:
 * <ul>
 * <li>file:./config/</li>
 * <li>file:./</li>
 * <li>classpath:config/</li>
 * <li>classpath:</li>
 * </ul>
 * The list is ordered by precedence (properties defined in locations higher in the list
 * override those defined in lower locations).
 * <p>
 * Alternative search locations and names can be specified using
 * {@link #setSearchLocations(String)} and {@link #setSearchNames(String)}.
 * <p>
 * Additional files will also be loaded based on active profiles. For example if a 'web'
 * profile is active 'application-web.properties' and 'application-web.yml' will be
 * considered.
 * <p>
 * The 'spring.config.name' property can be used to specify an alternative name to load
 * and the 'spring.config.location' property can be used to specify alternative search
 * locations or specific files.
 * <p>
 * 从默认的位置加载配置文件,将其加入上下文的environment变量中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

这个监听器默认的从注释中

标签所示的几个位置加载配置文件,并将其加入 上下文的 environment变量中。当然也可以通过配置指定。

debug跳过 listeners.environmentPrepared(environment); 这一行,查看environment属性,果真如上面所说的,配置文件的配置信息已经添加上来了。

image-20201120185147578

# 第三步:初始化应用上下文

在SpringBoot工程中,应用类型分为三种,如下代码所示。

  public enum WebApplicationType {
      /**
       * 应用程序不是web应用,也不应该用web服务器去启动
       */
      NONE,
      /**
       * 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
       */
      SERVLET,
      /**
       * 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。
       */
      REACTIVE
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

对应三种应用类型,SpringBoot项目有三种对应的应用上下文,我们以web工程为例,即其上下文为AnnotationConfigServletWebServerApplicationContext。

  public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
          + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
  public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
          + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
  public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
          + "annotation.AnnotationConfigApplicationContext";
  
  
  protected ConfigurableApplicationContext createApplicationContext() {
      Class<?> contextClass = this.applicationContextClass;
      if (contextClass == null) {
          try {
              switch (this.webApplicationType) {
                  case SERVLET:
                      contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                      break;
                  case REACTIVE:
                      contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                      break;
                  default:
                      contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
              }
          } catch (ClassNotFoundException ex) {
              throw new IllegalStateException(
                      "Unable create a default ApplicationContext, "
                              + "please specify an ApplicationContextClass",
                      ex);
          }
      }
      return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

我们先看一下AnnotationConfigServletWebServerApplicationContext的设计

image-20201120191403428

应用上下文可以理解成IoC容器的高级表现形式,应用上下文确实是在IoC容器的基础上丰富了一些高级功能。

应用上下文对IoC容器是持有的关系。他的一个属性beanFactory就是IoC容器(DefaultListableBeanFactory)。所以他们之间是持有,和扩展的关系。

接下来看GenericApplicationContext类

  public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
      private final DefaultListableBeanFactory beanFactory;
      ...
      public GenericApplicationContext() {
          this.beanFactory = new DefaultListableBeanFactory();
      }
      ...
  }
1
2
3
4
5
6
7
8

beanFactory正是在AnnotationConfigServletWebServerApplicationContext实现的接口GenericApplicationContext中定义的。在上面createApplicationContext()方法中的, BeanUtils.instantiateClass(contextClass) 这个方法中,不但初始化了AnnotationConfigServletWebServerApplicationContext类,也就是我们的上下文context,同样也触发了GenericApplicationContext类的构造函数,从而beanFactory(IOC容器)也创建了。

仔细看他的构造函数,有没有发现一个很熟悉的类DefaultListableBeanFactory,没错,DefaultListableBeanFactory就是IoC容器真实面目了。在后面的refresh()方法分析中,DefaultListableBeanFactory是无处不在的存在感。

debug跳过createApplicationContext()方法。

image-20201120191843346

如上图所示,context就是我们熟悉的上下文(也有人称之为容器,都可以,看个人爱好和理解),beanFactory就是我们所说的IoC容器的真实面孔了。细细感受下上下文和容器的联系和区别,对于我们理解源码有很大的帮助。在我们学习过程中,我们也是将上下文和容器严格区分开来的。

# 第四步:刷新应用上下文前的准备阶段

# prepareContext()方法

前面我们介绍了SpringBoot 启动流程run()方法的前三步,接下来再来介绍:

第四步:刷新应用上下文前的准备阶段。也就是prepareContext()方法。

首先看prepareContext()方法。

  private void prepareContext(ConfigurableApplicationContext context,
                              ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                              ApplicationArguments applicationArguments, Banner printedBanner) {
      //设置容器环境
      context.setEnvironment(environment);
      //执行容器后置处理
      postProcessApplicationContext(context);
      //执行容器中的 ApplicationContextInitializer 包括spring.factories和通过三种方式自定义的
      applyInitializers(context);
      //向各个监听器发送容器已经准备好的事件
      listeners.contextPrepared(context);
      if (this.logStartupInfo) {
          logStartupInfo(context.getParent() == null);
          logStartupProfileInfo(context);
      }
  
  
      // Add boot specific singleton beans
      //将main函数中的args参数封装成单例Bean,注册进容器
      context.getBeanFactory().registerSingleton("springApplicationArguments",
              applicationArguments);
      //将 printedBanner 也封装成单例,注册进容器
      if (printedBanner != null) {
          context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
      }
  
  
      // Load the sources
      Set<Object> sources = getAllSources();
      Assert.notEmpty(sources, "Sources must not be empty");
      //加载我们的启动类,将启动类注入容器
      load(context, sources.toArray(new Object[0]));
      //发布容器已加载事件
      listeners.contextLoaded(context);
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

首先看这行 Set sources = getAllSources(); 在getAllSources()中拿到了我们的启动类。   我们重点讲解这行 load(context, sources.toArray(new Object[0])); ,其他的方法请参阅注释。

跟进load()方法,看源码

protected void load(ApplicationContext context, Object[] sources) {
    if (logger.isDebugEnabled()) {
        logger.debug(
                "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    //创建 BeanDefinitionLoader
    BeanDefinitionLoader loader = createBeanDefinitionLoader(
            getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    loader.load();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# getBeanDefinitionRegistry()

继续看getBeanDefinitionRegistry()方法的源码

 private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
     if (context instanceof BeanDefinitionRegistry) {
         return (BeanDefinitionRegistry) context;
     }
     ...
 }
1
2
3
4
5
6

这里将我们前文创建的上下文强转为BeanDefinitionRegistry,他们之间是有继承关系的。BeanDefinitionRegistry定义了很重要的方法registerBeanDefinition(),该方法将BeanDefinition注册进DefaultListableBeanFactory容器的beanDefinitionMap中。

# createBeanDefinitionLoader()

继续看createBeanDefinitionLoader()方法,最终进入了BeanDefinitionLoader类的构造方法,如下

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    Assert.notNull(registry, "Registry must not be null");
    Assert.notEmpty(sources, "Sources must not be empty");
    this.sources = sources;
    //注解形式的Bean定义读取器 比如:@Configuration @Bean @Component @Controller @Service等等
    this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    //XML形式的Bean定义读取器
    this.xmlReader = new XmlBeanDefinitionReader(registry);
    if (isGroovyPresent()) {
        this.groovyReader = new GroovyBeanDefinitionReader(registry);
    }
    //类路径扫描器
    this.scanner = new ClassPathBeanDefinitionScanner(registry);
    //扫描器添加排除过滤器
    this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

先记住上面的三个属性,上面三个属性在,BeanDefinition的Resource定位,和BeanDefinition的注册中起到了很重要的作用。

# loader.load();

跟进load()方法

private int load(Object source) {
    Assert.notNull(source, "Source must not be null");
    // 从Class加载
    if (source instanceof Class<?>) {
        return load((Class<?>) source);
    }
    // 从Resource加载
    if (source instanceof Resource) {
        return load((Resource) source);
    }
    // 从Package加载
    if (source instanceof Package) {
        return load((Package) source);
    }
    // 从 CharSequence 加载 ???
    if (source instanceof CharSequence) {
        return load((CharSequence) source);
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

当前我们的主类会按Class加载。

继续跟进load()方法。

private int load(Class<?> source) {
    if (isGroovyPresent()
            && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
        // Any GroovyLoaders added in beans{} DSL can contribute beans here
        GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
                GroovyBeanDefinitionSource.class);
        load(loader);
    }
    if (isComponent(source)) {
        //将 启动类的 BeanDefinition注册进 beanDefinitionMap
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

isComponent(source)判断主类是不是存在@Component注解,主类@SpringBootApplication是一个组合注解,包含@Component。

this.annotatedReader.register(source);跟进register()方法,最终进到AnnotatedBeanDefinitionReader类的doRegisterBean()方法。

<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
        @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {


    //将指定的类 封装为AnnotatedGenericBeanDefinition
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
        return;
    }


    abd.setInstanceSupplier(instanceSupplier);
    // 获取该类的 scope 属性
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));


    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    if (qualifiers != null) {
        for (Class<? extends Annotation> qualifier : qualifiers) {
            if (Primary.class == qualifier) {
                abd.setPrimary(true);
            }
            else if (Lazy.class == qualifier) {
                abd.setLazyInit(true);
            }
            else {
                abd.addQualifier(new AutowireCandidateQualifier(qualifier));
            }
        }
    }
    for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
        customizer.customize(abd);
    }


    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // 将该BeanDefinition注册到IoC容器的beanDefinitionMap中
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

在该方法中将主类封装成AnnotatedGenericBeanDefinition

BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);方法将BeanDefinition注册进beanDefinitionMap

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
        throws BeanDefinitionStoreException {
    // Register bean definition under primary name.
    // primary name 其实就是id吧
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    // Register aliases for bean name, if any.
    // 然后就是注册别名
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

继续跟进registerBeanDefinition()方法。

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {


    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");


    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            // 最后一次校验了
            // 对bean的Overrides进行校验,还不知道会在哪处理这些overrides
            ((AbstractBeanDefinition) beanDefinition).validate();
        } catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Validation of bean definition failed", ex);
        }
    }
    // 判断是否存在重复名字的bean,之后看允不允许override
    // 以前使用synchronized实现互斥访问,现在采用ConcurrentHashMap
    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {
        //如果该类不允许 Overriding 直接抛出异常
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                            "': There is already [" + existingDefinition + "] bound.");
        } else if (existingDefinition.getRole() < beanDefinition.getRole()) {
            // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
            if (logger.isWarnEnabled()) {
                logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                        "' with a framework-generated bean definition: replacing [" +
                        existingDefinition + "] with [" + beanDefinition + "]");
            }
        } else if (!beanDefinition.equals(existingDefinition)) {
            if (logger.isInfoEnabled()) {
                logger.info("Overriding bean definition for bean '" + beanName +
                        "' with a different definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Overriding bean definition for bean '" + beanName +
                        "' with an equivalent definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        //注册进beanDefinitionMap
        this.beanDefinitionMap.put(beanName, beanDefinition);
    } else {
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                if (this.manualSingletonNames.contains(beanName)) {
                    Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;
                }
            }
        } else {
            // Still in startup registration phase
            //如果仍处于启动注册阶段,注册进beanDefinitionMap
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            this.manualSingletonNames.remove(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }


    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

最终来到DefaultListableBeanFactory类的registerBeanDefinition()方法,DefaultListableBeanFactory类还熟悉吗?相信大家一定非常熟悉这个类了。DefaultListableBeanFactory是IoC容器的具体产品。

仔细看这个方法registerBeanDefinition(),首先会检查是否已经存在,如果存在并且不允许被覆盖则直接抛出异常。不存在的话就直接注册进beanDefinitionMap中。

debug跳过prepareContext()方法,可以看到,启动类的BeanDefinition已经注册进来了。

image-20201123105500473

OK,到这里启动流程的第五步就算讲完了,因为启动类BeanDefinition的注册流程和后面我们自定义的BeanDefinition的注册流程是一样的。这先介绍一遍这个流程,后面熟悉了这个流程就好理解了。后面马上就到最最最重要的refresh()方法了。

# 第五步:刷新应用上下文(IOC容器的初始化过程)

首先我们要知道到IoC容器的初始化过程,主要分下面三步:

1 BeanDefinition的Resource定位
2 BeanDefinition的载入
3 向IoC容器注册BeanDefinition
1
2
3

在上一小节介绍了prepareContext()方法,在准备刷新阶段做了什么工作。

接下来我们主要从refresh()方法中总结IoC容器的初始化过程。 从run方法的,refreshContext()方法一路跟下去,最终来到AbstractApplicationContext类的refresh()方法。

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        //刷新上下文环境
        prepareRefresh();
        // Tell the subclass to refresh the internal bean factory.
        //这里是在子类中启动 refreshBeanFactory() 的地方
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // Prepare the bean factory for use in this context.
        //准备bean工厂,以便在此上下文中使用
        prepareBeanFactory(beanFactory);
        try {
            // Allows post-processing of the bean factory in context subclasses.
            //设置 beanFactory 的后置处理
            postProcessBeanFactory(beanFactory);
            // Invoke factory processors registered as beans in the context.
            //调用 BeanFactory 的后处理器,这些处理器是在Bean 定义中向容器注册的
            invokeBeanFactoryPostProcessors(beanFactory);
            // Register bean processors that intercept bean creation.
            //注册Bean的后处理器,在Bean创建过程中调用
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            //对上下文中的消息源进行初始化
            initMessageSource();
            // Initialize event multicaster for this context.
            //初始化上下文中的事件机制
            initApplicationEventMulticaster();
            // Initialize other special beans in specific context subclasses.
            //初始化其他特殊的Bean
            onRefresh();
            // Check for listener beans and register them.
            //检查监听Bean并且将这些监听Bean向容器注册
            registerListeners();
            // Instantiate all remaining (non-lazy-init) singletons.
            //实例化所有的(non-lazy-init)单件
            finishBeanFactoryInitialization(beanFactory);
            // Last step: publish corresponding event.
            //发布容器事件,结束Refresh过程
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();
            // Reset 'active' flag.
            cancelRefresh(ex);
            // Propagate exception to caller.
            throw ex;
        } finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

从以上代码中我们可以看到,refresh()方法中所作的工作也挺多,我们没办法面面俱到,主要根据IoC容器的初始化步骤进行分析,所以我们主要介绍重要的方法,其他的请看注释。

# obtainFreshBeanFactory();

在启动流程的第三步:初始化应用上下文。中我们创建了应用的上下文,并触发了GenericApplicationContext类的构造方法如下所示,创建了beanFactory,也就是创建了DefaultListableBeanFactory类。

 public GenericApplicationContext() {
     this.beanFactory = new DefaultListableBeanFactory();
 }
1
2
3

关于obtainFreshBeanFactory()方法,其实就是拿到我们之前创建的beanFactory。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    //刷新BeanFactory
    refreshBeanFactory();
    //获取beanFactory
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}
1
2
3
4
5
6
7
8
9
10

从上面代码可知,在该方法中主要做了三个工作,刷新beanFactory,获取beanFactory,返回beanFactory。

首先看一下refreshBeanFactory()方法,跟下去来到GenericApplicationContext类的refreshBeanFactory()发现也没做什么。

@Override
protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
        throw new IllegalStateException(
                "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    }
    this.beanFactory.setSerializationId(getId());
}
TIPS:
  1,AbstractApplicationContext类有两个子类实现了refreshBeanFactory(),但是在前面第三步初始化上下文的时候,
实例化了GenericApplicationContext类,所以没有进入AbstractRefreshableApplicationContext中的refreshBeanFactory()方法。
  2,this.refreshed.compareAndSet(false, true) 
  这行代码在这里表示:GenericApplicationContext只允许刷新一次   
  这行代码,很重要,不是在Spring中很重要,而是这行代码本身。首先看一下this.refreshed属性: 
private final AtomicBoolean refreshed = new AtomicBoolean(); 
  java J.U.C并发包中很重要的一个原子类AtomicBoolean。通过该类的compareAndSet()方法可以实现一段代码绝对只实现一次的功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

image-20201123110420011

# prepareBeanFactory(beanFactory);

从字面意思上可以看出准备BeanFactory。

看代码,具体看看做了哪些准备工作。这个方法不是重点,看注释吧。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Tell the internal bean factory to use the context's class loader etc.
    // 配置类加载器:默认使用当前上下文的类加载器
    beanFactory.setBeanClassLoader(getClassLoader());
    // 配置EL表达式:在Bean初始化完成,填充属性的时候会用到
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    // 添加属性编辑器 PropertyEditor
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));


    // Configure the bean factory with context callbacks.
    // 添加Bean的后置处理器
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    // 忽略装配以下指定的类
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);


    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    // 将以下类注册到 beanFactory(DefaultListableBeanFactory) 的resolvableDependencies属性中
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);


    // Register early post-processor for detecting inner beans as ApplicationListeners.
    // 将早期后处理器注册为application监听器,用于检测内部bean
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));


    // Detect a LoadTimeWeaver and prepare for weaving, if found.
    //如果当前BeanFactory包含loadTimeWeaver Bean,说明存在类加载期织入AspectJ,
    // 则把当前BeanFactory交给类加载期BeanPostProcessor实现类LoadTimeWeaverAwareProcessor来处理,
    // 从而实现类加载期织入AspectJ的目的。
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }


    // Register default environment beans.
    // 将当前环境变量(environment) 注册为单例bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    // 将当前系统配置(systemProperties) 注册为单例Bean
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    // 将当前系统环境 (systemEnvironment) 注册为单例Bean
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

# 四、postProcessBeanFactory(beanFactory);

postProcessBeanFactory()方法向上下文中添加了一系列的Bean的后置处理器。

后置处理器工作的时机是在所有的beanDenifition加载完成之后,bean实例化之前执行。简单来说Bean的后置处理器可以修改BeanDefinition的属性信息。

# 五、invokeBeanFactoryPostProcessors(beanFactory);(重点)

IoC容器的初始化过程包括三个步骤,在invokeBeanFactoryPostProcessors()方法中完成了IoC容器初始化过程的三个步骤。

1,第一步:Resource定位

在SpringBoot中,我们都知道他的包扫描是从主类所在的包开始扫描的,prepareContext()方法中,会先将主类解析成BeanDefinition,然后在refresh()方法的invokeBeanFactoryPostProcessors()方法中解析主类的BeanDefinition获取basePackage的路径。这样就完成了定位的过程。其次SpringBoot的各种starter是通过SPI扩展机制实现的自动装配,SpringBoot的自动装配同样也是在invokeBeanFactoryPostProcessors()方法中实现的。还有一种情况,在SpringBoot中有很多的@EnableXXX注解,细心点进去看的应该就知道其底层是@Import注解,在invokeBeanFactoryPostProcessors()方法中也实现了对该注解指定的配置类的定位加载。

常规的在SpringBoot中有三种实现定位,第一个是主类所在包的,第二个是SPI扩展机制实现的自动装配(比如各种starter),第三种就是@Import注解指定的类。(对于非常规的不说了)

2,第二步:BeanDefinition的载入

在第一步中说了三种Resource的定位情况,定位后紧接着就是BeanDefinition的分别载入。所谓的载入就是通过上面的定位得到的basePackage,SpringBoot会将该路径拼接成:classpath*:com/itheima/**/*.class这样的形式,然后一个叫做xPathMatchingResourcePatternResolver的类会将该路径下所有的.class文件都加载进来,然后遍历判断是不是有@Component注解,如果有的话,就是我们要装载的BeanDefinition。大致过程就是这样的了。

TIPS:
    @Configuration,@Controller,@Service等注解底层都是@Component注解,只不过包装了一层罢了。
1
2

​ 3、第三个过程:注册BeanDefinition

这个过程通过调用上文提到的BeanDefinitionRegister接口的实现来完成。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过上文的分析,我们可以看到,在IoC容器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。比如DefaultListableBeanFactory 中的beanDefinitionMap属性。

OK,总结完了,接下来我们通过代码看看具体是怎么实现的。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    ...
}
// PostProcessorRegistrationDelegate类
public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    ...
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    ...
}
// PostProcessorRegistrationDelegate类
private static void invokeBeanDefinitionRegistryPostProcessors(
        Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {


    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}
// ConfigurationClassPostProcessor类
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    ...
    processConfigBeanDefinitions(registry);
}
// ConfigurationClassPostProcessor类
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    ...
    do {
        parser.parse(candidates);
        parser.validate();
        ...
    }
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

一路跟踪调用栈,来到ConfigurationClassParser类的parse()方法。

// ConfigurationClassParser类
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<>();
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 如果是SpringBoot项目进来的,bd其实就是前面主类封装成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition接口的实现类)
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            } else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        } catch (BeanDefinitionStoreException ex) {
            throw ex;
        } catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }
    // 加载默认的配置---》(对springboot项目来说这里就是自动装配的入口了)
    processDeferredImportSelectors();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

看上面的注释,在前面的prepareContext()方法中,我们详细介绍了我们的主类是如何一步步的封装成AnnotatedGenericBeanDefinition,并注册进IoC容器的beanDefinitionMap中的。

image-20201123112947411

继续沿着parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());方法跟下去

看doProcessConfigurationClass()方法。(SpringBoot的包扫描的入口方法,重点)

// ConfigurationClassParser类
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
// ConfigurationClassParser类
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    ...
    // Recursively process the configuration class and its superclass hierarchy.
    //递归地处理配置类及其父类层次结构。
    SourceClass sourceClass = asSourceClass(configClass);
    do {
        //递归处理Bean,如果有父类,递归处理,直到顶层父类
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
    while (sourceClass != null);


    this.configurationClasses.put(configClass, configClass);
}
// ConfigurationClassParser类
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {


    // Recursively process any member (nested) classes first
    //首先递归处理内部类,(SpringBoot项目的主类一般没有内部类)
    processMemberClasses(configClass, sourceClass);


    // Process any @PropertySource annotations
    // 针对 @PropertySource 注解的属性配置处理
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        } else {
            logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }


    // Process any @ComponentScan annotations
    // 根据 @ComponentScan 注解,扫描项目中的Bean(SpringBoot 启动类上有该注解)
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        for (AnnotationAttributes componentScan : componentScans) {
            // The config class is annotated with @ComponentScan -> perform the scan immediately
            // 立即执行扫描,(SpringBoot项目为什么是从主类所在的包扫描,这就是关键了)
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // Check the set of scanned definitions for any further config classes and parse recursively if needed
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                // 检查是否是ConfigurationClass(是否有configuration/component两个注解),如果是,递归查找该类相关联的配置类。
                // 所谓相关的配置类,比如@Configuration中的@Bean定义的bean。或者在有@Component注解的类上继续存在@Import注解。
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }


    // Process any @Import annotations
    //递归处理 @Import 注解(SpringBoot项目中经常用的各种@Enable*** 注解基本都是封装的@Import)
    processImports(configClass, sourceClass, getImports(sourceClass), true);


    // Process any @ImportResource annotations
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        String[] resources = importResource.getStringArray("locations");
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }


    // Process individual @Bean methods
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }


    // Process default methods on interfaces
    processInterfaces(configClass, sourceClass);


    // Process superclass, if any
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }


    // No superclass -> processing is complete
    return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

我们大致说一下这个方法里面都干了什么

TIPS:
  在以上代码的parse(bdCand.getBeanClassName(), holder.getBeanName());会进行递归调用,
因为当Spring扫描到需要加载的类会进一步判断每一个类是否满足是@Component/@Configuration注解的类,
如果满足会递归调用parse()方法,查找其相关的类。
  同样的processImports(configClass, sourceClass, getImports(sourceClass), true);
通过@Import注解查找到的类同样也会递归查找其相关的类。
  两个递归在debug的时候会很乱,用文字叙述起来更让人难以理解,所以,我们只关注对主类的解析,及其类的扫描过程。
1
2
3
4
5
6
7

上面代码中 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(... 获取主类上的@PropertySource注解),解析该注解并将该注解指定的properties配置文件中的值存储到Spring的 Environment中,Environment接口提供方法去读取配置文件中的值,参数是properties文件中定义的key值。

Set componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 解析主类上的@ComponentScan注解,后面的代码将会解析该注解并进行包扫描。

processImports(configClass, sourceClass, getImports(sourceClass), true); 解析主类上的@Import注解,并加载该注解指定的配置类。

TIPS:


  在spring中好多注解都是一层一层封装的,比如@EnableXXX,是对@Import注解的二次封装。@SpringBootApplication注解=@ComponentScan+@EnableAutoConfiguration+@Import+@Configuration+@Component。@Controller,@Service等等是对@Component的二次封装。。。
1
2
3
4

继续向下看:

Set scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

进入该方法

// ComponentScanAnnotationParser类
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
            componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    ...
    // 根据 declaringClass (如果是SpringBoot项目,则参数为主类的全路径名)
    if (basePackages.isEmpty()) {
        basePackages.add(ClassUtils.getPackageName(declaringClass));
    }
    ...
    // 根据basePackages扫描类
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}
1
2
3
4
5
6
7
8
9
10
11
12
13

发现有两行重要的代码

为了验证代码中的注释,debug,看一下declaringClass,如下图所示确实是我们的主类的全路径名。

image-20211115095137666

跳过这一行,继续debug,查看basePackages,该set集合中只有一个,就是主类所在的路径。

image-20211115095345963

TIPS:
  为什么只有一个还要用一个集合呢,因为我们也可以用@ComponentScan注解指定扫描路径。
1
2

到这里呢IoC容器初始化三个步骤的第一步,Resource定位就完成了,成功定位到了主类所在的包。

接着往下看 return scanner.doScan(StringUtils.toStringArray(basePackages)); Spring是如何进行类扫描的。进入doScan()方法。

 1 // ComponentScanAnnotationParser类
 2 protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
 3     Assert.notEmpty(basePackages, "At least one base package must be specified");
 4     Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
 5     for (String basePackage : basePackages) {
 6         // 从指定的包中扫描需要装载的Bean
 7         Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
 8         for (BeanDefinition candidate : candidates) {
 9             ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
10             candidate.setScope(scopeMetadata.getScopeName());
11             String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
12             if (candidate instanceof AbstractBeanDefinition) {
13                 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
14             }
15             if (candidate instanceof AnnotatedBeanDefinition) {
16                 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
17             }
18             if (checkCandidate(beanName, candidate)) {
19                 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
20                 definitionHolder =
21                         AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
22                 beanDefinitions.add(definitionHolder);
23                 //将该 Bean 注册进 IoC容器(beanDefinitionMap)
24                 registerBeanDefinition(definitionHolder, this.registry);
25             }
26         }
27     }
28     return beanDefinitions;
29 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

这个方法中有两个比较重要的方法,第7行 Set candidates = findCandidateComponents(basePackage); 从basePackage中扫描类并解析成BeanDefinition,拿到所有符合条件的类后在第24行 registerBeanDefinition(definitionHolder, this.registry); 将该类注册进IoC容器。也就是说在这个方法中完成了IoC容器初始化过程的第二三步,BeanDefinition的载入,和BeanDefinition的注册。

# findCandidateComponents(basePackage);

跟踪调用栈

复制代码
 1 // ClassPathScanningCandidateComponentProvider类
 2 public Set<BeanDefinition> findCandidateComponents(String basePackage) {
 3     ...
 4     else {
 5         return scanCandidateComponents(basePackage);
 6     }
 7 }
 8 // ClassPathScanningCandidateComponentProvider类
 9 private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
10     Set<BeanDefinition> candidates = new LinkedHashSet<>();
11     try {
12         //拼接扫描路径,比如:classpath*:com/itheima/**/*.class
13         String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
14                 resolveBasePackage(basePackage) + '/' + this.resourcePattern;
15         //从 packageSearchPath 路径中扫描所有的类
16         Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
17         boolean traceEnabled = logger.isTraceEnabled();
18         boolean debugEnabled = logger.isDebugEnabled();
19         for (Resource resource : resources) {
20             if (traceEnabled) {
21                 logger.trace("Scanning " + resource);
22             }
23             if (resource.isReadable()) {
24                 try {
25                     MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
26                     // //判断该类是不是 @Component 注解标注的类,并且不是需要排除掉的类
27                     if (isCandidateComponent(metadataReader)) {
28                         //将该类封装成 ScannedGenericBeanDefinition(BeanDefinition接口的实现类)类
29                         ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
30                         sbd.setResource(resource);
31                         sbd.setSource(resource);
32                         if (isCandidateComponent(sbd)) {
33                             if (debugEnabled) {
34                                 logger.debug("Identified candidate component class: " + resource);
35                             }
36                             candidates.add(sbd);
37                         } else {
38                             if (debugEnabled) {
39                                 logger.debug("Ignored because not a concrete top-level class: " + resource);
40                             }
41                         }
42                     } else {
43                         if (traceEnabled) {
44                             logger.trace("Ignored because not matching any filter: " + resource);
45                         }
46                     }
47                 } catch (Throwable ex) {
48                     throw new BeanDefinitionStoreException(
49                             "Failed to read candidate component class: " + resource, ex);
50                 }
51             } else {
52                 if (traceEnabled) {
53                     logger.trace("Ignored because not readable: " + resource);
54                 }
55             }
56         }
57     } catch (IOException ex) {
58         throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
59     }
60     return candidates;
61 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

在第13行将basePackage拼接成classpath*:org/springframework/boot/demo/**/*.class,在第16行的getResources(packageSearchPath);方法中扫描到了该路径下的所有的类。然后遍历这些Resources,在第27行判断该类是不是 @Component 注解标注的类,并且不是需要排除掉的类。在第29行将扫描到的类,解析成ScannedGenericBeanDefinition,该类是BeanDefinition接口的实现类。OK,IoC容器的BeanDefinition载入到这里就结束了。

回到前面的doScan()方法,debug看一下结果(截图中所示的就是定位的需要交给Spring容器管理的类)。

# registerBeanDefinition(definitionHolder, this.registry);

查看registerBeanDefinition()方法。是不是有点眼熟,在前面介绍prepareContext()方法时,我们详细介绍了主类的BeanDefinition是怎么一步一步的注册进DefaultListableBeanFactory的beanDefinitionMap中的。完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableFactory中已经建立了整个Bean的配置信息,而这些BeanDefinition已经可以被容器使用了。他们都在BeanbefinitionMap里被检索和使用。容器的作用就是对这些信息进行处理和维护。这些信息是容器简历依赖反转的基础。

 protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
     BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
 }
1
2
3

OK,到这里IoC容器的初始化过程的三个步骤就梳理完了。当然这只是针对SpringBoot的包扫描的定位方式的BeanDefinition的定位,加载,和注册过程。前面我们说过,还有两种方式@Import和SPI扩展实现的starter的自动装配。

# @Import注解的解析过程

现在大家也应该知道了,各种@EnableXXX注解,很大一部分都是对@Import的二次封装(其实也是为了解耦,比如当@Import导入的类发生变化时,我们的业务系统也不需要改任何代码)。

我们又要回到上文中的ConfigurationClassParser类的doProcessConfigurationClass方法的第68行processImports(configClass, sourceClass, getImports(sourceClass), true);,跳跃性比较大。上面解释过,我们只针对主类进行分析,因为这里有递归。

processImports(configClass, sourceClass, getImports(sourceClass), true);中configClass和sourceClass参数都是主类相对应的。

首先看getImports(sourceClass);

 private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
     Set<SourceClass> imports = new LinkedHashSet<>();
     Set<SourceClass> visited = new LinkedHashSet<>();
     collectImports(sourceClass, imports, visited);
     return imports;
 }
1
2
3
4
5
6

debug

image-20211115095439448

两个呢是主类上的@SpringBootApplication中的@Import注解指定的类

接下来,是不是要进行执行了

记下来再回到ConfigurationClassParser类的parse(Set configCandidates):

public void parse(Set<BeanDefinitionHolder> configCandidates) {
        for (BeanDefinitionHolder holder : configCandidates) {
            BeanDefinition bd = holder.getBeanDefinition();
            try {
                if (bd instanceof AnnotatedBeanDefinition) {
                    parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
                }
                else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                    parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
                }
                else {
                    parse(bd.getBeanClassName(), holder.getBeanName());
                }
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
            }
        }


        // 去执行组件类
        this.deferredImportSelectorHandler.process();
    }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

点进process方法:

    public void process() {
            List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
            this.deferredImportSelectors = null;
            try {
                if (deferredImports != null) {
                    DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                    deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                    deferredImports.forEach(handler::register);
                    // 继续点击进去
                    handler.processGroupImports();
                }
            }
            finally {
                this.deferredImportSelectors = new ArrayList<>();
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

继续点击handler.processGroupImports();

public void processGroupImports() {
            for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
                Predicate<String> exclusionFilter = grouping.getCandidateFilter();
                // 查看调用的getimports
                grouping.getImports().forEach(entry -> {
                    ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
                    try {
                        processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                                Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                                exclusionFilter, false);
                    }
                    catch (BeanDefinitionStoreException ex) {
                        throw ex;
                    }
                    catch (Throwable ex) {
                        throw new BeanDefinitionStoreException(
                                "Failed to process import candidates for configuration class [" +
                                        configurationClass.getMetadata().getClassName() + "]", ex);
                    }
                });
            }
        }
// 是不是很熟悉了  
public Iterable<Group.Entry> getImports() {
            for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
                // 调用了process方法
                this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                        deferredImport.getImportSelector());
            }
            return this.group.selectImports();
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

和之前介绍的process完美衔接

    public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                    () -> String.format("Only %s implementations are supported, got %s",
                            AutoConfigurationImportSelector.class.getSimpleName(),
                            deferredImportSelector.getClass().getName()));


            // 【1】,调用getAutoConfigurationEntry方法得到自动配置类放入autoConfigurationEntry对象中
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                    .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);


            // 【2】,又将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            // 【3】,遍历刚获取的自动配置类
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                // 这里符合条件的自动配置类作为key,annotationMetadata作为值放进entries集合
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 第六步:刷新应用上下文后的扩展接口

protected void afterRefresh(ConfigurableApplicationContext context,
        ApplicationArguments args) {
}
1
2
3

扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理。

上次更新: 2025/04/03, 11:07:08
源码剖析-自动配置
源码剖析-自定义Starter

← 源码剖析-自动配置 源码剖析-自定义Starter→

最近更新
01
tailwindcss
03-26
02
PaddleSpeech
02-18
03
whisper
02-18
更多文章>
Theme by Vdoing | Copyright © 2019-2025 跨境互联网 | 豫ICP备14016603号-5 | 豫公网安备41090002410995号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式