【源码】源码解析之 Spring Boot 应用启动配置原理

源码解析之 Spring Boot 应用启动配置原理

1. 启动原理

启动原理概述:
SpringApplication.run(主程序类)
1、new SpringApplicatiton(主程序类)

  • 判断是否 Web 应用
  • 加载并保存所有 ApplicationContextInitializer(META-INF/spring.factories)
  • 加载并保存所有 ApplicationListener
  • 获取到主程序类

2、run()

  • 回调所有的 SpringApplicationRunListener(META-INF/spring.factories) 的 starting
  • 获取 ApplicationArguments
  • 准备环境 & 回调所有监听器(SpringApplicationRunListener)的 environmentPrepared
  • 打印 banner 信息
  • 创建 IOC容器对象:
    AnnotationConfigEmbeddedWebApplicationContext(web 环境容器)
    AnnotationConfigApplicationContext(普通环境容器)

四个重要的事件回调机制:
配置在 META-INF/spring.factories 中

  • ApplicationContextInitializer
  • SpringApplicationRunListener

只需要放在容器中

  • ApplicationRunner
  • CommandLineRunner

以 DEBUG 方式运行 SpringApplication.run(SpringbootWebApplication.class, args)
1、首先执行 run 方法,传入主配置类和命令行参数

SpringApplication.run(SpringbootWebApplication.class, args);

2、将两个参数进行包装

public static ConfigurableApplicationContext run(Object source, String... args) {
    return run(new Object[]{source}, args);
}

3、new 出一个 SpringApplication,即SpringBoot应用启动分为两步创建SpringApplication对象运行 run

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return (new SpringApplication(sources)).run(args);
}

创建 SpringApplication 对象流程
创建对象
为对象中的属性赋上默认值

public SpringApplication(Object... sources) {
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = new HashSet();
    this.initialize(sources);
}

initialize(sources) 工作流程
1、判断 sources(主配置类)是否为空,并将主配置类保存

private void initialize(Object[] sources) {
	//判断 sources(主配置类)是否为空,并将主配置类保存
    if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
    }
	...
}

2、判断是否为 web 应用

this.webEnvironment = this.deduceWebEnvironment();

deduceWebEnvironment():
private boolean deduceWebEnvironment() {
    String[] var1 = WEB_ENVIRONMENT_CLASSES;
    int var2 = var1.length;

    for(int var3 = 0; var3 < var2; ++var3) {
        String className = var1[var3];
        if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
            return false;
        }
    }

    return true;
}

判断原理:判断 web 环境中需要的类是否存在,如下:
WEB_ENVIRONMENT_CLASSES:
private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};

3、this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)) 作用
META-INF/spring.factories 中找到所有的 ApplicationContextInitializer,然后保存

this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

getSpringFactoriesInstances():
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
    return this.getSpringFactoriesInstances(type, new Class[0]);
}

this.getSpringFactoriesInstances(type, new Class[0]):
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ... ...
    Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        
    return instances;
}

loadFactoryNames:
 public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();

    try {
        Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
        ... ...

spring.factories 存放了大量的 Initializer
在这里插入图片描述
在这里插入图片描述
4、从类路径下的 META-INF/spring.factories 配置的所有 ApplicationListener

this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

5、从多个配置类中找到有 main 方法的主配置类

this.mainApplicationClass = this.deduceMainApplicationClass();

deduceMainApplicationClass():
private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
        StackTraceElement[] var2 = stackTrace;
        int var3 = stackTrace.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            StackTraceElement stackTraceElement = var2[var4];
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    ... ...
查找哪一个配置类含 main 方法,即主配置类: if ("main".equals(stackTraceElement.getMethodName()))

此时,SpringApplication 对象创建完成

运行 run 方法,启动应用流程
run 方法运行过程总代码:

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    this.configureHeadlessProperty();
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting();

    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        Banner printedBanner = this.printBanner(environment);
        context = this.createApplicationContext();
        new FailureAnalyzers(context);
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        listeners.finished(context, (Throwable)null);
        stopWatch.stop();
        if (this.logStartupInfo) {
           (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }

        return context;
   } catch (Throwable var9) {
        this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
        throw new IllegalStateException(var9);
    }
}

1、创建 StopWatch ,开始停止的监听

StopWatch stopWatch = new StopWatch();
stopWatch.start();

2、声明 IOC容器以及属性值等

ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
this.configureHeadlessProperty();

3、获取 SpringApplicationRunListener

SpringApplicationRunListeners listeners = this.getRunListeners(args);

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
    return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
仍然是从类路径下的 META-INF/spring.factories 中获取

4、将所有 SpringApplicationRunListener 的 starting() 方法进行回调

public void starting() {
	Iterator var1 = this.listeners.iterator();

    while(var1.hasNext()) {
        SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
        listener.starting();
    }
}

5、封装命令行参数

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

6、准备环境

ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
    this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
    listeners.environmentPrepared((ConfigurableEnvironment)environment);
    ... ...

    return (ConfigurableEnvironment)environment;
}
创建环境完成后,回调 SpringApplicationRunListener 的 environmentPrepared() 方法:环境准备完成

5、打印 banner 图标

Banner printedBanner = this.printBanner(environment);

在这里插入图片描述
6、创建 ApplicationContext

context = this.createApplicationContext();

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            contextClass = Class.forName(this.webEnvironment ? "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext" : "org.springframework.context.annotation.AnnotationConfigApplicationContext");
        } 
        ... ...
    }

    return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
}
决定是创建 web IOC 容器(AnnotationConfigEmbeddedWebApplicationContext)还是普通容器(AnnotationConfigApplicationContext)
同时利用 BeanUtils 工具利用反射返回:return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);

7、异常分析报告

new FailureAnalyzers(context);

8、准备上下文

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    this.postProcessApplicationContext(context);
    this.applyInitializers(context);
    ... ...
准备上下文环境;将 environment 保存到 IOC 中
回调之前保存的所有 ApplicationContextInitializer 的 initialize() 方法
回调所有的 SpringApplicationRunListener 的 contextPrepared() 方法

prepareContext运行完成以后,回调所有的 SpringApplicationRunListener 的 contextLoaded() 方法

控制台日志输出 环境已准备好
在这里插入图片描述
9、刷新容器

this.refreshContext(context);

IOC容器初始化,扫描所有的组件、配置类等

各种组件等已在控制台日志输出,如果是 Web 应用还会创建嵌入式的 Tomcat
在这里插入图片描述
10、this.afterRefresh(context, applicationArguments);

this.afterRefresh(context, applicationArguments);

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
    this.callRunners(context, args);
}

private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        Iterator var4 = (new LinkedHashSet(runners)).iterator();

        while(var4.hasNext()) {
            Object runner = var4.next();
            if (runner instanceof ApplicationRunner) {
                this.callRunner((ApplicationRunner)runner, args);
        }

        if (runner instanceof CommandLineRunner) {
            this.callRunner((CommandLineRunner)runner, args);
        }
    }

}

从 IOC 容器中获取所有的 ApplicationRunner 和 CommandLineRunner 进行回调
ApplicationRunner 先进行,CommandLineRunner 后进行

11、所有的 SpringApplicationRunListener 回调 finished() 方法

listeners.finished(context, (Throwable)null);

public void finished(ConfigurableApplicationContext context, Throwable exception) {
    Iterator var3 = this.listeners.iterator();

    while(var3.hasNext()) {
        SpringApplicationRunListener listener = (SpringApplicationRunListener)var3.next();
        this.callFinishedListener(listener, context, exception);
    }

}

12、返回启动的 IOC 容器

return context;

2. 事件监听机制测试

实现 ApplicationContextInitializer,监听 ConfigurableApplicationContext 容器的启动

package com.example.demo.listener;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.printf("ApplicationContextInitializer initialize......" + configurableApplicationContext);
    }
}

实现 SpringApplicationRunListener

package com.example.demo.listener;

import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
    @Override
    public void starting() {
        System.out.printf("SpringApplicationRunListener starting...");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment configurableEnvironment) {
        Object o = configurableEnvironment.getSystemProperties().get("os.name")
        System.out.printf("SpringApplicationRunListener environmentPrepared..." + o);
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.printf("SpringApplicationRunListener contextPrepared...");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.printf("SpringApplicationRunListener contextLoaded...");
    }

    @Override
    public void finished(ConfigurableApplicationContext configurableApplicationContext, Throwable throwable) {
        System.out.printf("SpringApplicationRunListener finished...");
    }

	//必须有一个有参构造器
	public HelloSpringApplicationRunListener(SpringApplication application, String[] args){
        
    }
}

实现 ApplicationRunner

package com.example.demo.listener;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;

@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        System.out.printf("ApplicationRunner run...");
    }
}

实现 CommandLineRunner

package com.example.demo.listener;

import org.springframework.boot.CommandLineRunner;

@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... strings) throws Exception {
        System.out.printf("CommandLineRunner run...");
    }
}

将 ApplicationRunner 、CommandLineRunner 注册到容器中:添加 @Component 注解

将 ApplicationContextInitializer、SpringApplicationRunListener 完成配置如下:
在类路径下建立 META-INF 文件夹,创建文件 spring.factories
文件配置如下:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.example.demo.listener.HelloApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
com.example.demo.listener.HelloSpringApplicationRunListener

测试结果:
在这里插入图片描述
在这里插入图片描述

时间:2019.6.25 20:56

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值