springboot启动流程原理

简介: springboot启动流程原理

springboot的启动流程主要包括以下部分:

一、SpringBootApplication启动类

启动类的常见写法

@SpringBootApplication
public class SpringmvcApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringmvcApplication.class, args);
    }
}

需要重点关注的是@SpringBootApplication注解和SpringApplication.run(SpringmvcApplication.class, args)方法。

二、SpringApplication.run方法

1.实例化SpringApplication

SpringApplication.run(SpringmvcApplication.class, args)调用的方法;

//启动类调的run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    //调的是下面的,参数是数组的run方法
    return run(new Class<?>[] { primarySource }, args);
}
//和上面的方法区别在于第一个参数是一个数组
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    //实际上new一个SpringApplication实例,调的是一个实例方法run()
    return new SpringApplication(primarySources).run(args);
}

判断当前项目类型

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    //断言primarySources不能为null,如果为null,抛出异常提示
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //启动类传入的Class
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //判断当前项目类型,有三种:NONE、SERVLET、REACTIVE
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //设置ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //判断主类,初始化入口类
    this.mainApplicationClass = deduceMainApplicationClass();
}
//判断主类
private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

设置初始化器

从类路径中META-INF/spring.factories加载对应的配置文件,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。

//设置初始化器(Initializer),最后会调用这些初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));

//设置初始化器(Initializer),最后会调用这些初始化器
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}
// 这里的入参type就是ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // 使用Set保存names来避免重复元素
    Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 根据names来进行实例化
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    // 对实例进行排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}
// 这里的入参type就是ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // 使用Set保存names来避免重复元素
    Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 根据names来进行实例化
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    // 对实例进行排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

设置监听器类。 主要的方式和设置初始化类一样。

整个流程:

总结

1、构造SpringApplication对象

主要判断容器类型是否为servlet、reactive还是none

2、获取ApplicationContextInitializer对象

从META-INF/spring.factories获取容器初始化需要的初始化对象

3、获取ApplicationListener对象

从META-INF/spring.factories获取监听器的配置类

2.执行run()方法

实例化后就是执行run()方法;

主要包括以下步骤:

  • 第一步:获取并启动监听器
  • 第二步:根据SpringApplicationRunListeners以及参数来准备环境
  • 创建Environment环境变量
  • 会查询系统变量,环境变量还有启动参数
  • 第三步:创建Spring容器
  • 第四步:Spring容器前置处理
  • 第五步:刷新容器
  • 将启动类作为配置类传给run()方法,作为spring容器的配置类
  • 第六步:Spring容器后置处理
  • 第七步:发出结束执行的事件
  • 第八步:执行Runners
  • 在springboot的启动过程中会执行ApplicationRunner和CommandLineRunner类型的bean
    1.获取Spring容器中的ApplicationRunner类型的Bean
    2.获取Spring容器中的CommandLineRunner类型的Bean
    3.执行它们的run()

会在springcontext创建之前就包environment环境变量准备好;

public ConfigurableApplicationContext run(String... args) {
    // 计时工具
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    // 第一步:获取并启动监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 第二步:根据SpringApplicationRunListeners以及参数来准备环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
        Banner printedBanner = printBanner(environment);
        // 第三步:创建Spring容器
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 第四步:Spring容器前置处理
        prepareContext(context, environment, listeners, applicationArguments,printedBanner);
        // 第五步:刷新容器
        refreshContext(context);
     // 第六步:Spring容器后置处理
        afterRefresh(context, applicationArguments);
      // 第七步:发出结束执行的事件
        listeners.started(context);
        // 第八步:执行Runners
        this.callRunners(context, applicationArguments);
        stopWatch.stop();
        // 返回容器
        return context;
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, exceptionReporters, ex);
        throw new IllegalStateException(ex);
    }
}

3.项目启动可选的初始化操作

  • 通过ApplicationRunner和CommandLineRunner
  • 通过InitializingBean注解
  • 通过监听事件来处理

参考资料

  1. SpringBoot启动流程是怎样的?:https://juejin.cn/post/6895341123816914958#heading-8
  2. SpringBoot 源码解析 (二)----- Spring Boot精髓:启动流程源码分析:https://www.cnblogs.com/java-chen-hao/p/11829344.html#_label0_0

todo

  1. 文章细节待补充待优化


目录
相关文章
|
1月前
|
前端开发 Java 数据安全/隐私保护
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
文章通过一个简单的SpringBoot项目,详细介绍了前后端如何实现用户登录功能,包括前端登录页面的创建、后端登录逻辑的处理、使用session验证用户身份以及获取已登录用户信息的方法。
118 2
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
|
15天前
|
Java Spring 容器
springboot @RequiredArgsConstructor @Lazy解决循环依赖的原理
【10月更文挑战第15天】在Spring Boot应用中,循环依赖是一个常见问题,当两个或多个Bean相互依赖时,会导致Spring容器陷入死循环。本文通过比较@RequiredArgsConstructor和@Lazy注解,探讨它们解决循环依赖的原理和优缺点。@RequiredArgsConstructor通过构造函数注入依赖,使代码更简洁;@Lazy则通过延迟Bean的初始化,打破创建顺序依赖。两者各有优势,需根据具体场景选择合适的方法。
36 4
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的销售项目流程化管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的销售项目流程化管理系统附带文章源码部署视频讲解等
65 3
|
4月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
117 0
|
2月前
|
Java 应用服务中间件 API
Vertx高并发理论原理以及对比SpringBoot
Vertx 是一个基于 Netty 的响应式工具包,不同于传统框架如 Spring,它的侵入性较小,甚至可在 Spring Boot 中使用。响应式编程(Reactive Programming)基于事件模式,通过事件流触发任务执行,其核心在于事件流 Stream。相比多线程异步,响应式编程能以更少线程完成更多任务,减少内存消耗与上下文切换开销,提高 CPU 利用率。Vertx 适用于高并发系统,如 IM 系统、高性能中间件及需要较少服务器支持大规模 WEB 应用的场景。随着 JDK 21 引入协程,未来 Tomcat 也将优化支持更高并发,降低响应式框架的必要性。
Vertx高并发理论原理以及对比SpringBoot
|
23天前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
22 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
29天前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
136 2
|
2月前
|
Java 开发者 数据格式
【Java笔记+踩坑】SpringBoot基础4——原理篇
bean的8种加载方式,自动配置原理、自定义starter开发、SpringBoot程序启动流程解析
【Java笔记+踩坑】SpringBoot基础4——原理篇
|
24天前
|
JSON 前端开发 JavaScript
优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程
本文介绍如何使用 Spring Boot 3.3 实现职责链模式,优化电商订单处理流程。通过将订单处理的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来,实现了代码的解耦和灵活扩展。具体实现包括订单请求类 `OrderRequest`、抽象处理器类 `OrderHandler`、具体处理器实现(如 `OrderValidationHandler`、`VerifyCouponHandler` 等)、以及初始化职责链的配置类 `OrderChainConfig`。
|
3月前
|
XML Java 应用服务中间件
SpringBoot启动流程解析
SpringBoot启动流程解析
44 0