springBoot---第二部分:SpringBoot源码剖析(一)

SpringBoot源码环境构建

下载源码

环境准备

编译源码

进⼊spring-boot源码根⽬录
执⾏mvn命令: mvn clean install -DskipTests -Pfast
        mvn clean -rf :spring-boot-gradle-plugin
 
// 跳过测试⽤例,会下载⼤量 jar 包(时间会长一些)

--------------------------------------------------------------------------------亲测有效

导入IDEA

打开pom.xml关闭maven代码检查
<properties> 
    <revision>2.2.9.RELEASE</revision> 
    <main.basedir>${basedir}</main.basedir>     
    <disable.checks>true</disable.checks> 
</properties> 

新建一个module

新建一个Controller

@RestController
public class TestController {

    @RequestMapping("/test")
    public String test(){
        System.out.println("源码环境构建成功...");
        return "源码环境构建成功";
    }
}

源码剖析-依赖管理

(1)为什么导入dependency时不需要指定版本?
在Spring Boot入门程序中,项目pom.xml文件有两个核心依赖,分别是
spring-boot-starter-parent
spring-boot-starter-web,
关于这两个依赖的相关介绍具体如下

spring-boot-starter-parent

在chapter01项目中的pom.xml文件中找到spring-boot-starter-parent依赖,示例代码如下:
      <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
上述代码中,将spring-boot-starter-parent依赖作为Spring Boot项目的统一父项目依赖管理,并
将项目版本号统一为2.2.9.RELEASE,该版本号根据实际开发需求是可以修改的
 
使用“Ctrl+鼠标左键”进入并查看spring-boot-starter-parent底层源文件,先看
spring-boot-starter-parent做了哪些事
 
首先看 spring-boot-starter-parent 的 properties 节点
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
<java.version>1.8</java.version>
<resource.delimiter>@</resource.delimiter>
<!-- delimiter that doesn't clash with Spring ${} placeholders -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF- 8</project.reporting.outputEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
在这里 spring-boot-starter-parent 定义了:
1. 工程的Java版本为 1.8 。
2. 工程代码的编译源文件编码格式为 UTF-8
3. 工程编译后的文件编码格式为 UTF-8
4. Maven打包编译的版本
 
再来看 spring-boot-starter-parent 的「build」节点
接下来看POM的 build 节点,分别定义了 resources 资源和 pluginManagement
我们详细看一下 resources 节点,里面定义了资源过滤,针对 application 的 yml 、 properties
式进行了过滤,可以支持支持不同环境的配置,
比如 application-dev.yml 、 application-test.yml 、 application-dev.properties 、 application-dev.properties 等等。
 
pluginManagement 则是引入了相应的插件和对应的版本依赖
 
最后来看spring-boot-starter-parent的父依赖 spring-boot-dependencies
 
spring-boot-dependencies的properties节点
我们看定义POM,这个才是SpringBoot项目的真正管理依赖的项目,里面定义了SpringBoot相关的版
 
spring-boot-dependencies的dependencyManagement节点
在这里,dependencies定义了SpringBoot版本的依赖的组件以及相应版本。 
 

总结:

spring-boot-starter-parent 通过继承 spring-boot-dependencies
从而实现了SpringBoot的版本依赖管理,
所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了,
这也就是在 Spring Boot 项目中部分依赖不需要写版本号的原因 

spring-boot-starter-web

(2)spring-boot-starter-parent父依赖启动器的主要作用是进行版本统一管理,那么项目
运行依赖的JAR包是从何而来的?
 
1.查看spring-boot-starter-web依赖文件源码,核心代码具体如下
 
从上述代码可以发现,spring-boot-starter-web依赖启动器的主要作用是打包了Web开发场景所需的底
层所有依赖(基于依赖传递,当前项目也存在对应的依赖jar包)
 
正是如此,在pom.xml中引入spring-boot-starter-web依赖启动器时,就可以实现Web场景开发,而
不需要额外导入Tomcat服务器以及其他Web依赖文件等。
当然,这些引入的依赖文件的版本号还是由spring-boot-starter-parent父依赖进行的统一管理
 
Spring Boot除了提供有上述介绍的Web依赖启动器外,还提供了其他许多开发场景的相关依赖,
 
我们可以打开Spring Boot官方文档,搜索“Starters”关键字查询场景依赖启动器

源码剖析-自动配置

自动配置:根据我们添加的jar包依赖,会自动将一些配置类的bean注册进ioc容器,我们可以需要的地
方使用@autowired或者@resource等注解来使用它。
 
问题:Spring Boot到底是如何进行自动配置的,都把哪些组件进行了自动配置?
 
Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法,
 
@SpringBootApplication :
SpringBoot 应用标注在某个类上说明这个类是 SpringBoot 的主配置类,
SpringBoot 就应该运行这个类的 main() 方法启动 SpringBoot 应用。

@SpringBootApplication

下面,查看@SpringBootApplication内部源码进行分析 ,核心代码具体如下
@Target(ElementType.TYPE) // 注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中
@Retention(RetentionPolicy.RUNTIME) // 表示注解的生命周期,Runtime运行时
@Documented // 表示注解可以记录在javadoc中
@Inherited // 表示可以被子类继承该注解
// ------------------------------------------------
@SpringBootConfiguration // 标明该类为配置类
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) // 注解扫描
public @interface SpringBootApplication {


    // 根据class来排除特定的类,使其不能加入spring容器,传入参数value类型是class类型。
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};


    // 根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全类名字符串数组
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};


    // 指定扫描包,参数是包名的字符串数组。
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    // 扫描特定的包,参数类似是Class类型数组。
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

     
    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;

}

从上述源码可以看出,@SpringBootApplication注解是一个组合注解,前面 4 个是注解的元数据信
息, 我们主要看后面 3 个注解:
@SpringBootConfiguration、
@EnableAutoConfiguration、
@ComponentScan
三个核心注解,关于这三个核心注解的相关说明具体如下

@SpringBootConfiguration

@SpringBootConfiguration :
SpringBoot 的配置类,标注在某个类上,表示这是一个 SpringBoot的配置类。
查看@SpringBootConfiguration注解源码,核心代码具体如下。
 
从上述源码可以看出,@SpringBootConfiguration注解内部有一个核心注解@Configuration,该注解
是Spring框架提供的,表示当前类为一个配置类(XML配置文件的注解表现形式),并可以被组件扫描
器扫描。
由此可见,
@SpringBootConfiguration注解的作用与@Configuration注解相同
都是标识一个可以被组件扫描器扫描的配置类
只不过@SpringBootConfiguration是被Spring Boot进行了重新封装命名而已

@EnableAutoConfiguration 

@EnableAutoConfiguration = 
@AutoConfigurationPackage // 自动配置包 + 
@Import(AutoConfigurationImportSelector.class) // Spring的底层注解@Import,给容器中导入一个组件

大体流程:
1.点击

   2.进入DeferredImportSelector

   

  3.根据调用栈确定入口,DeferredImportSelectorGrouping$getImports

   4.   进入group.process,处理配置类

      

     

    5.   进入group.selectImports,处理配置类 

     


Spring 中有很多以 Enable 开头的注解,其作用就是借助 @Import 来收集并注册特定场景相关的
Bean ,并加载到 IOC 容器。
@EnableAutoConfiguration就是借助@Import来收集所有符合自动配置条件的bean定义
并加载到IoC容器。

@AutoConfigurationPackage

 

@AutoConfigurationPackage
自动配置包,它也是一个组合注解,其中最重要的注解是@Import(AutoConfigurationPackages.Registrar.class) ,
它是 Spring 框架的底层注解,它的作用就是给容器中导入某个组件类,
例如
@Import(AutoConfigurationPackages.Registrar.class) ,它就是将 Registrar 这个组件类导入到容器中,
可查看 Registrar 类中 registerBeanDefinitions 方法:
我们对 new PackageImport(metadata).getPackageName() 进行检索,看看其结果是什么?
 
再看register方法

AutoConfigurationPackages.Registrar这个类就干一个事,
注册一个 Bean ,这个 Bean 就是org.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages
它有一个参数,这个参数是使用了 @AutoConfigurationPackage 这个注解的类所在的包路径,保存自动配置
类以供之后的使用,比如给 JPA entity 扫描器用来扫描开发人员通过注解 @Entity 定义的 entity类。

@Import(AutoConfigurationImportSelector.class)

@Import({AutoConfigurationImportSelector.class})
AutoConfigurationImportSelector 这个类导入到 Spring 容器中,
AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的
@Configuration配置都加载到当前 SpringBoot 创建并使用的 IOC 容器( ApplicationContext )中。
可以看到 AutoConfigurationImportSelector
重点是实现了 DeferredImportSelector 接口和各种Aware 接口,
然后 DeferredImportSelector 接口又继承了 ImportSelector 接口。
其不光实现了 ImportSelector 接口,还实现了很多其它的 Aware 接口,分别表示在某个时机会被回调。

确定自动配置实现逻辑的入口方法

跟自动配置逻辑相关的入口方法在 DeferredImportSelectorGrouping 类的 getImports 方法处,
因此我们就从 DeferredImportSelectorGrouping 类的 getImports 方法来开始分析SpringBoot的自
动配置源码好了
 
先看一下 getImports 方法代码
 
 
标 【1】 处的的代码是我们分析的重中之重,
自动配置的相关的绝大部分逻辑全在这里了。那么
this.group.process(deferredImport.getConfigurationClass().getMetadata(),deferredImport.getImportSelector()) ;
主要做的事情就是在 this.group 即AutoConfigurationGroup 对象的 process 方法中,
传入的 AutoConfigurationImportSelector对象来选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类,就是这么个事情
 
注:
AutoConfigurationGroup:
是AutoConfigurationImportSelector的内部类,主要用来处理自动配置相关的逻辑,拥有process和selectImports方法,
然后拥有entries和autoConfigurationEntries集合属性,
这两个集合分别存储被处理后的符合条件的自动配置类,我们知道这些就足够了;
 
AutoConfigurationImportSelector:承担自动配置的绝大部分逻辑,负责选择一些符合条件的自动配置类;
 
metadata:标注在SpringBoot启动类上的@SpringBootApplication注解元数据
 
标【2】的this.group.selectImports的方法主要是针对前面的process方法处理后的自动配置类再进一
步有选择的选择导入
 
再进入到AutoConfigurationImportSelector$AutoConfigurationGroup的pross方法:
上面代码中我们再来看标 【1】 的方法 getAutoConfigurationEntry ,这个方法主要是用来获取自动
配置类有关,承担了自动配置的主要逻辑。直接上代码: 
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        // 获取是否有配置spring.boot.enableautoconfiguration属性,默认返回true
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);

        // 【1】得到spring.factories文件配置的所有自动配置类
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

        // 利用LinkedHashSet移除重复的配置类
        configurations = removeDuplicates(configurations);

        // 得到要排除的自动配置类,比如注解属性exclude的配置类
        // 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
        // 将会获取到exclude = FreeMarkerAutoConfiguration.class的注解数据
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);

        // 检查要被排除的配置类,因为有些不是自动配置类,故要抛出异常
        checkExcludedClasses(configurations, exclusions);

        // 【2】将要排除的配置类移除
        configurations.removeAll(exclusions);

        // 【3】因为从spring.factories文件获取的自动配置类太多,如果有些不必要的自动配置类都加载进内存,会造成内存浪费,因此这里需要进行过滤
        configurations = filter(configurations, autoConfigurationMetadata);

        // 【4】获取了符合条件的自动配置类后,此时触发AutoConfigurationImportEvent事件,
        // 目的是告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类
        fireAutoConfigurationImportEvents(configurations, exclusions);

        // 【5】将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回
        return new AutoConfigurationEntry(configurations, exclusions);
    }
这个方法中有一个重要方法 loadFactoryNames ,这个方法是让 SpringFactoryLoader 去加载一些组件的名字。
 
继续点开 loadFactory 方法 

 

 

 

 

有选择的导入自动配置类

this.group.selectImports 方法是如何进一步有选择的导入自动配置类的。直接看代码:
可以看到, selectImports 方法主要是针对经过排除掉 exclude 的和被
AutoConfigurationImportFilter 接口过滤后的满足条件的自动配置类再进一步排除 exclude 的自
动配置类,然后再排序 
最后,我们再总结下SpringBoot自动配置的原理,主要做了以下事情:
1. 从spring.factories配置文件中加载自动配置类;
 
2. 加载的自动配置类中排除掉 @EnableAutoConfiguration 注解的 exclude 属性指定的自动配置类;
 
3. 然后再用 AutoConfigurationImportFilter 接口去过滤自动配置类是否符合其标注注解(若有标注的话)
@ConditionalOnClass ,
@ConditionalOnBean 和
@ConditionalOnWebApplication 的条件,
若都符合的话则返回匹配结果;
 
4. 然后触发 AutoConfigurationImportEvent 事件,告诉 ConditionEvaluationReport 条件评估报告器对象来分别记录符合条件和 exclude 的自动配置类。
 
5. 最后spring再将最后筛选后的自动配置类导入IOC容器中
 

@ComponentScan注解

@ComponentScan使用
主要是从定义的扫描路径中,找出标识了需要装配的类自动装配到spring 的bean容器中
常用属性如下:
  • basePackages、value:指定扫描路径,如果为空则以@ComponentScan注解的类所在的包为基本的扫描路径
  • basePackageClasses:指定具体扫描的类
  • includeFilters:指定满足Filter条件的类
  • excludeFilters:指定排除Filter条件的类
includeFilters和excludeFilters 的FilterType可选:ANNOTATION=注解类型 默认、
ASSIGNABLE_TYPE(指定固定类)、ASPECTJ(ASPECTJ类型)、REGEX(正则表达式)、CUSTOM(自定义类
型),自定义的Filter需要实现TypeFilter接口
 
@ComponentScan的配置如下:
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
借助excludeFilters将TypeExcludeFillter及FilterType这两个类进行排除
当前@ComponentScan注解没有标注basePackages及value,所以扫描路径默认为@ComponentScan
注解的类所在的包为基本的扫描路径(也就是标注了@SpringBootApplication注解的项目启动类所在的路径)
 
 
抛出疑问:@EnableAutoConfiguration注解是通过@Import注解加载了自动配置固定的bean
@ComponentScan注解自动进行注解扫描
 
那么真正根据包扫描,把组件类生成实例对象存到IOC容器中,又是怎么来完成的?

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

SpringApplication() 构造方法

 

 

 

 

deduceWebApplicationType()

返回类型是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))

看看 getSpringFactoriesInstances 都干了什么,看源码,有一个方法很重要 loadFactoryNames()
这个方法很重要,这个方法是spring-core中提供的从META-INF/spring.factories中获取指定的类
(key)的同一入口方法。在这里,
获取的是key为 org.springframework.context.ApplicationContextInitializer 的类。
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容器全生命周期的监听,当然也
可以自定义监听事件 

run(args)

/**
     * 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();
        // 【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);
            // 【6、刷新应用上下文后的扩展接口】
            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;
    }

 

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

 

EventPublishingRunListener监听器是Spring容器的启动监听器。
listeners.starting(); 开启了监听事件。

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

ConfigurableEnvironment environment = getOrCreateEnvironment()

configureEnvironment(environment, applicationArguments.getSourceArgs())

listeners.environmentPrepared(environment)

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

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

 

应用上下文可以理解成IoC容器的高级表现形式,应用上下文确实是在IoC容器的基础上丰富了一
些高级功能。
应用上下文对IoC容器是持有的关系。他的一个属性beanFactory就是IoC容器
(DefaultListableBeanFactory)。所以他们之间是持有,和扩展的关系
 
接下来看GenericApplicationContext类

 

 

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

prepareContext()方法
前面我们介绍了SpringBoot 启动流程run()方法的前三步,接下来再来介绍:
第四步:刷新应用上下文前的准备阶段。也就是prepareContext()方法。
首先看prepareContext()方法
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // 设置容器环境
        context.setEnvironment(environment);
        // 执行容器后置处理
        postProcessApplicationContext(context);
        applyInitializers(context);
        // 向各个监听器发送容器已经准备好的事件
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 将main函数中的args参数封装成单例Bean,注册进容器
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            // 将 printedBanner 也封装成单例,注册进容器
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // 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);
    }
首先看这行 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();
    }
getBeanDefinitionRegistry()
继续看getBeanDefinitionRegistry()方法的源码
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
if (context instanceof BeanDefinitionRegistry) {
return (BeanDefinitionRegistry) context;
}
... }
这里将我们前文创建的上下文强转为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));
    }
先记住上面的三个属性,上面三个属性在,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());
    }
当前我们的主类会按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;
    }
isComponent(source)判断主类是不是存在@Component注解,主类
@SpringBootApplication是一个组合注解,包含@Component。
this.annotatedReader.register(source);跟进register()方法,最终进到
AnnotatedBeanDefinitionReader类的doRegisterBean()方法。
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) {
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            abd.setInstanceSupplier(supplier);
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
            abd.setScope(scopeMetadata.getScopeName());
            String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);
            AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
            int var10;
            int var11;
            if (qualifiers != null) {
                Class[] var9 = qualifiers;
                var10 = qualifiers.length;

                for(var11 = 0; var11 < var10; ++var11) {
                    Class<? extends Annotation> qualifier = var9[var11];
                    if (Primary.class == qualifier) {
                        abd.setPrimary(true);
                    } else if (Lazy.class == qualifier) {
                        abd.setLazyInit(true);
                    } else {
                        abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                    }
                }
            }

            if (customizers != null) {
                BeanDefinitionCustomizer[] var13 = customizers;
                var10 = customizers.length;

                for(var11 = 0; var11 < var10; ++var11) {
                    BeanDefinitionCustomizer customizer = var13[var11];
                    customizer.customize(abd);
                }
            }

            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
            definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
        }
    }
在该方法中将主类封装成AnnotatedGenericBeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);方法将
BeanDefinition注册进beanDefinitionMap

 

最终来到DefaultListableBeanFactory类的registerBeanDefinition()方法,
DefaultListableBeanFactory类还熟悉吗?相信大家一定非常熟悉这个类了。
DefaultListableBeanFactory是IoC容器的具体产品。仔细看这个方法registerBeanDefinition(),首先会检查是否已经存在,如果存在并且不允许
被覆盖则直接抛出异常。不存在的话就直接注册进beanDefinitionMap中。
debug跳过prepareContext()方法,可以看到,启动类的BeanDefinition已经注册进来了

第五步:刷新应用上下文

首先我们要知道到IoC容器的初始化过程,主要分下面三步:
1 BeanDefinition的Resource定位
2 BeanDefinition的载入
3 向IoC容器注册BeanDefinition
 
在上一小节介绍了prepareContext()方法,在准备刷新阶段做了什么工作。
接下来我们主要从refresh()方法中总结IoC容器的初始化过程。
从run方法的,refreshContext()方法一路跟下去,最终来到AbstractApplicationContext类的
refresh()方法
 
从以上代码中我们可以看到,refresh()方法中所作的工作也挺多,我们没办法面面俱到,主要
根据IoC容器的初始化步骤进行分析,所以我们主要介绍重要的方法,其他的请看注释。
obtainFreshBeanFactory(); 
在启动流程的第三步:初始化应用上下文。中我们创建了应用的上下文,并触发了
GenericApplicationContext类的构造方法如下所示,创建了beanFactory,也就是创建了
DefaultListableBeanFactory类。
关于obtainFreshBeanFactory()方法,其实就是拿到我们之前创建的beanFactory。
从上面代码可知,在该方法中主要做了三个工作,刷新beanFactory,获取beanFactory,返回
beanFactory。
首先看一下refreshBeanFactory()方法,跟下去来到GenericApplicationContext类的
refreshBeanFactory()发现也没做什么。

 

prepareBeanFactory(beanFactory);
从字面意思上可以看出准备BeanFactory。
看代码,具体看看做了哪些准备工作。这个方法不是重点,看注释吧。

 

 

postProcessBeanFactory(beanFactory);
postProcessBeanFactory()方法向上下文中添加了一系列的Bean的后置处理器。
后置处理器工作的时机是在所有的beanDenifition加载完成之后,bean实例化之前执行。简单来
说Bean的后置处理器可以修改BeanDefinition的属性信息。
invokeBeanFactoryPostProcessors(beanFactory);(重点)

 

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

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { 

}
扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写该方法。比如打
印一些启动结束log,或者一些其它后置处理。 
原文地址:https://www.cnblogs.com/denghy-301/p/14681114.html