首页 > 其他分享 >【SpringBoot每日学习 - 第二天】SpringApplication 启动类:方法篇一

【SpringBoot每日学习 - 第二天】SpringApplication 启动类:方法篇一

时间:2024-11-15 17:50:32浏览次数:3  
标签:SpringBoot Spring SpringApplication environment 第二天 applicationArguments context

SpringApplication 类是 Spring Boot 应用程序的核心类之一,负责启动和初始化整个 Spring Boot 应用。通过调用 SpringApplication.run() 方法,Spring Boot 会启动嵌入式的 Web 服务器(如 Tomcat)并创建 Spring 容器。SpringApplication 类具有一系列方法和配置项,允许开发者自定义应用的启动过程。

构造方法:

1. SpringApplication(Class<?>… primarySources)

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

解析:

作用:这是 Spring Boot 启动类的构造函数,用于初始化 SpringApplication 实例。primarySources
参数指定了 Spring 应用的主要配置类(通常是一个带有 @SpringBootApplication 注解的类)。这些配置类将用来初始化
Spring 应用上下文(ApplicationContext)。 默认行为:此构造函数调用了另一个构造函数,传入了
null(表示没有自定义的资源加载器),并传入 primarySources,即传入应用的主配置类。

2. SpringApplication(ResourceLoader resourceLoader, Class<?>… primarySources)

 
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

解析:

作用:
这个构造函数允许开发者传入一个自定义的resourceLoader,用于加载资源文件,并指定应用的主配置类(primarySources)。

关键点:
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)):
将 primarySources转换为一个不重复的 LinkedHashSet,确保应用的主配置类是唯一的。

this.webApplicationType = WebApplicationType.deduceFromClasspath():
根据类路径中的依赖,自动推断应用的 Web 类型(如SERVLET、REACTIVE)
setInitializers() 和 setListeners():这些方法会加载 Spring工厂实例,用于初始化和监听 Spring 应用的生命周期。
deduceMainApplicationClass():通过检查调用栈,推断出应用的主类(通常是带有@SpringBootApplication 注解的类)。

3. ConfigurableApplicationContext run(String… args)

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);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        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;
}

解析:

作用:
run() 方法是 Spring Boot 应用的入口,启动 Spring 应用上下文并执行一系列初始化操作。 关键点:
SpringApplicationRunListeners listeners = getRunListeners(args):获取应用启动监听器,并开始执行 starting()。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments):创建并配置环境,准备环境参数(如配置文件和命令行参数)。
Banner printedBanner = printBanner(environment):如果启用了 banner 模式(即 spring.banner.enabled 配置为 true),则打印 Banner。
context = createApplicationContext():创建 ApplicationContext,即 Spring 容器。
prepareContext():进行应用上下文的进一步准备,包括注册必要的 Bean、设置环境等。
refreshContext():刷新应用上下文,加载所有 Bean。
afterRefresh():完成刷新后的一些处理。
listeners.started(context):通知应用启动完成。
callRunners():调用所有的ApplicationRunner 或 CommandLineRunner 实例。
最后,run() 方法返回创建的ConfigurableApplicationContext,即 Spring 应用上下文。

4. prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    return environment;
}

解析:

作用:
准备并配置 Spring 应用的环境(如系统属性、环境变量、配置文件等)。
关键点:
ConfigurableEnvironment environment = getOrCreateEnvironment():获取或创建一个新的环境。
configureEnvironment(environment, applicationArguments.getSourceArgs()):配置环境,设置属性源和激活的 profile。
ConfigurationPropertySources.attach(environment):将所有配置属性源(如 application.properties)附加到环境中。
bindToSpringApplication(environment):将环境属性绑定到 SpringApplication 实例。

5. createApplicationContext()

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_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);
}

解析:

作用:
根据 Web 类型(如 SERVLET 或 REACTIVE),动态选择并创建适当的 ApplicationContext 类型。
关键点:
this.webApplicationType:根据 Web 类型推断是使用 Servlet 环境(传统的 Spring MVC)还是 Reactive 环境(如 WebFlux)。 根据 Web 类型(SERVLET、REACTIVE 或 NONE),选择合适的 ApplicationContext 类并通过反射创建实例。

6. printBanner(ConfigurableEnvironment environment)

private Banner printBanner(ConfigurableEnvironment environment) {
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
            : new DefaultResourceLoader(null);
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    if (this.bannerMode == Mode.LOG) {
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

解析:

作用:
根据配置的 bannerMode 打印应用的 Banner。Spring Boot 默认提供一个 ASCII 字符的 Banner。
关键点:
this.bannerMode == Banner.Mode.OFF:如果 Banner 模式被关闭,返回 null,即不打印Banner。
SpringApplicationBannerPrinter:负责将 Banner 打印到控制台或日志中,具体取决于配置。
bannerMode == Mode.LOG:如果 Banner 模式为日志,打印到日志中;否则,打印到标准输出(控制台)。

7. 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();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        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);
}

解析:

作用:
该方法是 run() 方法中的一部分,负责配置和初始化应用上下文(ApplicationContext)。它将环境设置到上下文中,并且进行一系列的初始化工作。
关键点:
context.setEnvironment(environment):将环境对象配置到应用上下文中,使得 Spring容器可以访问应用配置(如 application.properties 或 application.yml 中的属性)。
postProcessApplicationContext(context):如果有任何的后处理器(如自定义的ApplicationContext),这时可以应用。
applyInitializers(context):应用初始化器,通常用来在应用启动时进行一些额外的配置,比如设置某些属性、初始化Bean 等。
listeners.contextPrepared(context):通知启动监听器,表示应用上下文已经准备好了。
logStartupInfo() 和 logStartupProfileInfo():如果配置了日志输出,将记录应用的启动信息,如Spring 配置文件、激活的 Profiles、启动时间等。
beanFactory.registerSingleton(“springApplicationArguments”, applicationArguments):将 ApplicationArguments 注册为单例Bean,应用可以通过依赖注入访问这些命令行参数。 context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()):如果启用了懒加载(lazyInitialization配置为 true),将添加懒加载 Bean 的后处理器。

load(context, sources.toArray(new Object[0])):加载应用配置源,将 primarySources(即 @SpringBootApplication 注解的类)加载到上下文中。

8. refreshContext()

private void refreshContext(ConfigurableApplicationContext context) {
    refresh((ApplicationContext) context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

解析:

作用:
刷新应用上下文,初始化或重新初始化所有的 Bean,并完成 Spring 容器的启动。
关键点:
refresh((ApplicationContext) context):调用 Spring 的 refresh 方法,实际刷新容器,确保Spring 管理的所有 Bean 被初始化。
context.registerShutdownHook():如果配置了registerShutdownHook(默认为 true),则注册一个钩子方法,在 JVM 关闭时调用context.close(),进行 Spring 容器的关闭操作。

9. afterRefresh()

private void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments applicationArguments) {
    callRunners(context, applicationArguments);
}

解析:

作用:
在应用上下文刷新之后调用,此方法主要是调用应用中的所有 ApplicationRunner 和 CommandLineRunner实例。
关键点:
callRunners(context, applicationArguments):Spring Boot 提供的ApplicationRunner 和 CommandLineRunner是用于在应用启动完成后执行的接口。这些接口可以在容器启动后执行一些初始化任务(如启动时的批处理操作等)。

10. callRunners()

private void callRunners(ConfigurableApplicationContext context, ApplicationArguments applicationArguments) {
    List<ApplicationRunner> runners = getBeansOfType(context, ApplicationRunner.class);
    for (ApplicationRunner runner : runners) {
        runner.run(applicationArguments);
    }

    List<CommandLineRunner> commandLineRunners = getBeansOfType(context, CommandLineRunner.class);
    for (CommandLineRunner commandLineRunner : commandLineRunners) {
        commandLineRunner.run(applicationArguments.getSourceArgs());
    }
}

解析:

作用:
调用所有实现了 ApplicationRunner 和 CommandLineRunner 接口的 Beans。在 SpringBoot 启动完成后,这些 Beans 会被调用执行。
关键点:
getBeansOfType(context, ApplicationRunner.class):从 Spring 容器中获取所有 ApplicationRunner 类型的 Beans。
runner.run(applicationArguments):调用每个 ApplicationRunner 实例的 run()方法。ApplicationArguments 提供了命令行参数等信息。
getBeansOfType(context, CommandLineRunner.class):获取所有 CommandLineRunner 类型的Beans。CommandLineRunner 是另外一种 Spring Boot 提供的启动回调接口。

11. getRunListeners()

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

解析:

作用:
获取启动监听器(SpringApplicationRunListener)的实例,这些监听器会在 Spring Boot应用的各个生命周期阶段提供回调。
关键点:
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args):从 META-INF/spring.factories 中加载所有类型为 SpringApplicationRunListener 的实例。

12. getSpringFactoriesInstances()

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

解析:

作用:
加载 SpringFactories 文件中声明的所有实例。这些实例通常是一些工厂类或 Spring组件,用于提供扩展点或插件式的功能。
关键点:
SpringFactoriesLoader.loadFactoryNames(type, classLoader):从 META-INF/spring.factories 文件中加载指定类型的所有实现类。
AnnotationAwareOrderComparator.sort(instances):对加载的实例进行排序,以确保优先级(@Order注解)生效。

总结

SpringApplication 类的核心任务是启动 Spring Boot 应用,并对其生命周期进行管理。以下是该类的主要步骤:
初始化和配置环境:通过构造函数、环境配置、监听器和初始化器来设置应用的环境。 创建和准备上下文:根据 Web 类型(Servlet 或Reactive)创建 ApplicationContext,并将所有必要的配置源(如 primarySources)加载到上下文中。
启动和刷新应用上下文:通过调用 refreshContext() 和相关生命周期方法来启动和刷新 Spring 容器。
运行应用回调:启动时,执行实现 ApplicationRunner 或 CommandLineRunner接口的回调方法,通常用于初始化任务。 生命周期监听:通过 SpringApplicationRunListener 启动监听器来监控应用的启动过程。

标签:SpringBoot,Spring,SpringApplication,environment,第二天,applicationArguments,context
From: https://blog.csdn.net/qq_40759033/article/details/143803125

相关文章

  • 【SpringBoot每日学习 - 第一天】SpringApplication 启动类:属性篇
    SpringApplication类是SpringBoot应用启动的核心类之一,包含了大量的属性,控制着应用启动的各个方面。这些属性涵盖了从配置环境、应用上下文类型、Banner显示、启动日志、事件监听等多个方面。以下是SpringApplication类中重要属性的详细说明及其用途:静态属性DEFAUL......
  • SpringBoot 3.3.5 集成 mybatis-plus-boot-starter 3.4.2报错
    一、环境JDK:17SpringBoot:3.3.5Mybatis-Plus:3.4.2二、报错信息Considerthefollowing: Ifyouwantanembeddeddatabase(H2,HSQLorDerby),pleaseputitontheclasspath. Ifyouhavedatabasesettingstobeloadedfromaparticularprofileyoumayneed......
  • springboot整合ES及其基本使用
    Springboot整合ElasticSearch导入依赖<dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>${elasticsearch.version}</version>......
  • 基于Java+SpringBoot的校园资产管理
    关注底部领取源码源码编号:S324源码名称:基于SpringBoot的校园资产管理用户类型:双角色,用户、管理员主要技术:Java、Vue、ElementUl、SpringBoot运行环境:Windows/Mac、JDK1.8及以上运行工具:IDEA/Eclipse数 据 库:MySQL5.7及以上版本数据库表数量:11张表是否有毕业论文......
  • 基于Java+SpringBoot的人事管理系统
    关注底部领取源码源码编号:S323源码名称:基于SpringBoot的人事管理系统用户类型:双角色,员工、管理员主要技术:Java、Vue、ElementUl、SpringBoot运行环境:Windows/Mac、JDK1.8及以上运行工具:IDEA/Eclipse数 据 库:MySQL5.7及以上版本数据库表数量:13张表是否有毕业论文......
  • 基于Java+SpringBoot的老年一站式服务平台
    关注底部领取源码源码编号:S322源码名称:基于SpringBoot的老年一站式服务平台用户类型:多角色,用户、商家、员工、管理员主要技术:Java、Vue、ElementUl、SpringBoot运行环境:Windows/Mac、JDK1.8及以上运行工具:IDEA/Eclipse数 据 库:MySQL5.7及以上版本数据库表数量:18......
  • 如何深度学习SpringBoot?
    SpringBoot对于SpringBoot,我们都知道他的设计初衷是解决Spring各版本配置工作过于繁重的问题,简化初始搭建流程、降低开发难度而出现的。可以说用SpringBoot开发,我们在配置上是不用花费太多时间的。我们常常看到这样一种现象:面对Spring繁重配置工作,要是一位初学者仅仅掌握......
  • springBoot-RabbitMQ 高级特性(保姆级教程,一步一步带你熟悉RabbitMQ 相关高级特性)
    话不多说,看项目整体架构RabbitMQ高级特性保姆级教程好了,下面县开始贴生产者代码:publisher父依赖:<parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.7.18</versi......
  • SpringBoot+Vue3实现数据可视化大屏
    前端工程的地址:UserManagerFront:数据可视化前端(gitee.com)效果展示,可以展现出来了,样式可能还有一些丑。后端代码后端主要是拿到数据并对数据进行处理,按照前端需要的格式进行返回即可。importcom.njitzx.entity.Student;importcom.njitzx.entity.vo.*;import......
  • 201_springboot基于协同过滤的就业推荐系统
    目录系统展示开发背景代码实现项目案例 获取源码博主介绍:CodeMentor毕业设计领航者、全网关注者30W+群落,InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者,博客领航之星、开发者头条/腾讯云/AWS/Wired等平台优选内容创作者、深耕Web......