4

【Spring boot】启动过程源码分析 - zfcq

 1 year ago
source link: https://www.cnblogs.com/zfcq/p/16756777.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

启动过程结论

  • 推测web应用类型。
  • spi的方式获取BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationContextInitializer对象。
  • 通过调用栈推测出main()方法所在的类。
  • 调用启动方法:run(String... args)。后面的步骤在这个方法内部!
  • 触发SpringApplicationRunListener的starting()。
  • 创建Environment对象。
  • 触发SpringApplicationRunListener的environmentPrepared()。
  • 打印Banner。
  • 创建Spring容器对象(ApplicationContext)。
  • 利用ApplicationContextInitializer初始化Spring容器对象。
  • 触发SpringApplicationRunListener的contextPrepared()。
  • 调用DefaultBootstrapContext对象的close()。
  • 将启动类作为配置类注册到Spring容器中(load()方法)。
  • 触发SpringApplicationRunListener的contextLoaded()。
  • 刷新Spring容器。
  • 触发SpringApplicationRunListener的started()。
  • 调用ApplicationRunner和CommandLineRunner。
  • 触发SpringApplicationRunListener的ready()。
  • 上述过程抛异常了就触发SpringApplicationRunListener的failed()。
  • 第一步构造对象:new SpringApplication(primarySources)
  • 第二步调用SpringApplication.run(String... args)
// 我们自己写的main方法
@SpringBootApplication
public class ZfcqApp {
    public static void main(String[] args) {
        SpringApplication.run(ZfcqApp.class, args);
    }
}

/**
 * 他会调用SpringApplication的run方法
 * primarySource:我们传入的类的class
 * args:我们传入的参数,一般是启动的时候-D制定的参数
 */
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class<?>[] { primarySource }, args);
}

/**
 * 继续看内部调用的run方法
 */
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    // 俩步,第一步构造对象:new SpringApplication(primarySources)
    // 第二步调用SpringApplication.run(String... args)
    // 这里我们可以在main方法中分开俩步去写,从而可以在中间设置SpringApplication对象的信息
    return new SpringApplication(primarySources).run(args);
}

构造SpringApplication源码分析

/**
 * 调用俩个参数的构造方法
 * primarySources:我们主类的class
 */
public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

/**
 * 俩个产生的构造方法
 */
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 赋值到全局,这里第一次传入的是null
    this.resourceLoader = resourceLoader;
    // 主类存在的判断
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 赋值到全局,这里一般传入的是我们的main方法的类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

    // 推测WEB应用的类型(NONE、SERVLET、REACTIVE)
    this.webApplicationType = WebApplicationType.deduceFromClasspath();

    // 从spring.factories中获取BootstrapRegistryInitializer对象的值
    this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

    // 从spring.factories中获取ApplicationContextInitializer对象的值
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

    // 从spring.factories中获取ApplicationListener对象的值。非常重要的有一个是EnvironmentPostProcessorApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

    // 推测出main方法所在的类!SpringApplication.run(ZfcqApp.class, args);可以传任意的类!
    this.mainApplicationClass = deduceMainApplicationClass();
}

推测web应用类型

  • REACTIVE:web应用。
  • NONE:无Servlet,不是web应用。
  • SERVLET:除去上面俩种的其他应用。
static WebApplicationType deduceFromClasspath() {
    // 如果项目依赖中存在org.springframework.web.reactive.DispatcherHandler,并且不存在org.springframework.web.servlet.DispatcherServlet,那么应用类型为WebApplicationType.REACTIVE
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    // 如果项目依赖中不存在org.springframework.web.reactive.Dispatche#rHandler,也不存在org.springframework.web.servlet.DispatcherServlet,那么应用类型为WebApplicationType.NONE
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    // 否则,应用类型为WebApplicationType.SERVLET
    return WebApplicationType.SERVLET;
}

推测出Main类(main()方法所在的类)

  • 从调用栈中去获取main类!
private Class<?> deduceMainApplicationClass() {
    try {
        // 获取调用栈数组
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        // 找到调用栈中的main方法!
        for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

核心的启动方法:run(String... args)

public ConfigurableApplicationContext run(String... args) {
    // 开始时间
    long startTime = System.nanoTime();

    // 创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象。
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();

    // spring容器对象,开始的时候为空
    ConfigurableApplicationContext context = null;
    // 与awt有关,一般用不上
    configureHeadlessProperty();

    // spring boot的启动监听器
    // 从spring.factories中获取SpringApplicationRunListener对象的值
    // 默认会拿到EventPublishingRunListener,他会启动各个地方的ApplicationEvent事件。
    SpringApplicationRunListeners listeners = getRunListeners(args);

    // 发布开始启动的事件:ApplicationstartingEvent
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        // 把run方法的参数进行封装
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        // 准备environment对象:包括操作系统、jvm、ymal、properties....配置
        // 发布一个ApplicationEnvironmentPreparedEvent事件。
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

        // 默认spring.beaninfo.ignore为true,表示不需要jdk去缓存BeanInfo信息,spring自己缓存。
        // 这里是spring在创建bean的时候会利用jdk的一些工具来解析一个类的相关信息,jdk在解析一个类的信息的时候会进行缓存,这里就是禁止了jdk的缓存。
        configureIgnoreBeanInfo(environment);

        // 打印Banner
        Banner printedBanner = printBanner(environment);

        // 根据应用类型,创建spring容器
        context = createApplicationContext();
        // jdk9的一个机制,默认没做任何操作
        context.setApplicationStartup(this.applicationStartup);

        // 准备容器的操作
        // 利用ApplicationContextInitializer初始化spring容器
        // 发布ApplicationContextInitializedEvent(容器初始化完成)事件
        // 发布BootstrapContextClosedEvent(关闭引导容器)事件
        // 注册primarySources类(run方法存进来的配置类)
        // 发布ApplicationPreparedEvent事件
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

        // refresh容器:解析配置类、扫描、启动webServer。spring相关的逻辑!
        refreshContext(context);

        // 空方法,类似spring的onRefresh方法,可以由子类实现。
        afterRefresh(context, applicationArguments);

        // 启动的事件
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }

        // 发布一个ApplicationStartedEvent事件,表示spring已经启动完成
        listeners.started(context, timeTakenToStartup);

        // 从spring容器中获取ApplicationRunner和CommandLineRunner,并执行他的run方法。
        // 这俩个可以自己定义Bean
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        // 失败之后,发布一个失败的事件:ApplicationFailedEvent
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        // 计算下事件
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);

        // 一切都成功,发布ApplicationStartedEvent事件
        listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
        // 失败之后,发布一个失败的事件:ApplicationFailedEvent
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

创建引导启动器

private DefaultBootstrapContext createBootstrapContext() {
    // 构建一个DefaultBootstrapContext对象,这个对象是2.4.0之后才会有!
    DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
    // 利用bootstrapRegistryInitializers初始化bootstrapContext。可以在spring.factories中设置bootstrapRegistryInitializers
    this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
    return bootstrapContext;
}

准备environment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // Create and configure the environment   这句话是源码中本身的注释!
    // 创建ApplicationServletEnvironment。
    ConfigurableEnvironment environment = getOrCreateEnvironment();

    // 添加SimpleCommandLinePropertySource放在首位
    configureEnvironment(environment, applicationArguments.getSourceArgs());

    // 把所有的PropertySource封装为ConfigurationPropertySourcesPropertySource,然后添加到environment中,放在首位!
    ConfigurationPropertySources.attach(environment);

    // 发布ApplicationEnvironmentPreparedEvent(应用Environment准备完成)事件,表示环境已经准备好了。默认EnvironmentPostProcessorApplicationListener去处理这个事件!
    listeners.environmentPrepared(bootstrapContext, environment);

    // 把DefaultProperties放到最后
    DefaultPropertiesPropertySource.moveToEnd(environment);

    // 环境中spring.main.environment-prefix参数校验
    Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");

    // 环境中spring.main参数校验
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = convertEnvironment(environment);
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

准备spring容器的操作

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // 设置环境变量到spring容器
    context.setEnvironment(environment);

    // 设置在SpringApplication上的BeanNameGenerator、resourceLoader设置到spring容器中
    postProcessApplicationContext(context);

    // 使用ApplicationContextInitializer初始化spring容器
    applyInitializers(context);

    // 容器初始化完成,发布ApplicationContextInitializedEvent事件
    listeners.contextPrepared(context);

    // 关闭引导的容器
    bootstrapContext.close(context);

    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // 注册一些单例Bean
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }

    // spring容器设置AllowCircularReferences和allowBeanDefinitionOverriding
    if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
        ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // Load the sources
    // 拿到启动配置(run方法传递进来的)
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 将启动配置类解析为BeanDefinition注册到spring容器
    load(context, sources.toArray(new Object[0]));
    // 发布ApplicationPreparedEvent事件,表示已经启动好spring容器
    listeners.contextLoaded(context);
}

  • 你的点赞是我提高文章质量最大的动力!!!
  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 目前已经完成了并发编程、MySQL、spring源码、Mybatis的源码。可以在公众号下方菜单点击查看之前的文章!
  • 这个公众号的所有技术点,会分析的很深入!
  • 这个公众号,无广告!!!
    作者公众号.jpg

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK