【Spring Boot 源码学习】SpringApplication 的定制化介绍

Spring Boot 源码学习系列

SpringApplication 的定制化介绍

  • 一、引言
  • 二、往期内容
  • 三、主要内容
    • [1. 基础配置](#1. 基础配置)
      • [1.1 设置关闭 Banner](#1.1 设置关闭 Banner)
      • [1.2 设置自定义 Banner 打印对象](#1.2 设置自定义 Banner 打印对象)
      • [1.3 设置应用程序主入口类](#1.3 设置应用程序主入口类)
      • [1.4 设置用于创建应用程序上下文的工厂](#1.4 设置用于创建应用程序上下文的工厂)
      • [1.5 添加 BootstrapRegistry 初始化器实现](#1.5 添加 BootstrapRegistry 初始化器实现)
      • [1.6 设置或添加 ApplicationContext 初始化器实现](#1.6 设置或添加 ApplicationContext 初始化器实现)
      • [1.7 设置 ApplicationListener 实现](#1.7 设置 ApplicationListener 实现)
      • [1.8 设置要运行的Web应用程序的类型](#1.8 设置要运行的Web应用程序的类型)
      • [1.9 设置 SpringApplication 中 各种 boolean 变量](#1.9 设置 SpringApplication 中 各种 boolean 变量)
        • [1.9.1 setAllowBeanDefinitionOverriding 方法](#1.9.1 setAllowBeanDefinitionOverriding 方法)
        • [1.9.2 setAllowCircularReferences 方法](#1.9.2 setAllowCircularReferences 方法)
        • [1.9.3 setLazyInitialization 方法](#1.9.3 setLazyInitialization 方法)
        • [1.9.4 setHeadless 方法](#1.9.4 setHeadless 方法)
        • [1.9.5 setRegisterShutdownHook 方法](#1.9.5 setRegisterShutdownHook 方法)
        • [1.9.6 setLogStartupInfo 方法](#1.9.6 setLogStartupInfo 方法)
        • [1.9.7 setAddCommandLineProperties 方法](#1.9.7 setAddCommandLineProperties 方法)
        • [1.9.8 setAddConversionService 方法](#1.9.8 setAddConversionService 方法)
      • [1.10 设置默认的环境属性](#1.10 设置默认的环境属性)
      • [1.11 设置要使用的额外的配置文件值](#1.11 设置要使用的额外的配置文件值)
      • [1.12 设置 bean 名称生成器](#1.12 设置 bean 名称生成器)
      • [1.13 设置底层环境](#1.13 设置底层环境)
      • [1.14 设置资源加载器](#1.14 设置资源加载器)
      • [1.15 设置环境配置属性前缀](#1.15 设置环境配置属性前缀)
      • [1.16 设置应用启动指标对象](#1.16 设置应用启动指标对象)
    • [2. 数据源配置](#2. 数据源配置)
      • [2.1 通过 SpringApplication 构造方法参数](#2.1 通过 SpringApplication 构造方法参数)
      • [2.2 通过 setSources 方法](#2.2 通过 setSources 方法)
      • [2.3 合并配置源信息](#2.3 合并配置源信息)
  • 四、总结

一、引言

前面的博文,Huazie 带大家从 Spring Boot 的启动类 SpringApplication 上入手,了解了 SpringApplication 的实例化过程。这实例化构造过程中包含了各种初始化的操作,都是 Spring Boot 默认配置的。如果我们需要定制化配置,SpringApplication 也提供了相关的入口,且看下面的介绍。

二、往期内容

在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:

|-----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
| Spring Boot 源码学习 |
| Spring Boot 源码学习 | Spring Boot 项目介绍 |
| Spring Boot 源码学习 | Spring Boot 核心运行原理介绍 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】@EnableAutoConfiguration 注解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】@SpringBootApplication 注解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】走近 AutoConfigurationImportSelector |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】自动装配流程源码解析(上) |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】自动装配流程源码解析(下) |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】深入 FilteringSpringBootCondition |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】OnClassCondition 详解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】OnBeanCondition 详解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】OnWebApplicationCondition 详解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】@Conditional 条件注解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】HttpEncodingAutoConfiguration 详解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】RedisAutoConfiguration 详解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】JedisConnectionConfiguration 详解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】初识 SpringApplication |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】Banner 信息打印流程 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】自定义 Banner 信息打印 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】BootstrapRegistryInitializer 详解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】ApplicationContextInitializer 详解 |
| Spring Boot 源码学习 | 【Spring Boot 源码学习】ApplicationListener 详解 |

三、主要内容

针对 SpringApplication 的定制化配置,Spring Boot 中也提供了不同的方式,比如通过入口类、配置文件、环境变量、命令行参数等等。

注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。

1. 基础配置

所谓基础配置,即是可以直接通过 setadd 方法来进行参数的配置,这些 setadd 方法修改的配置都是 Spring Boot 预定义的一些参数,有些也可以在 application.properties 文件中进行配置。

在笔者的《Banner 信息打印流程》 中,第 2 小节就介绍了如何关闭 Banner 信息打印。

通过 SpringApplication 提供的 setBannerMode 方法,我们就可以在启动入口类中,这样来编写:

java 复制代码
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(DemoApplication.class);
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
    }
}

在笔者的《自定义 Banner 信息打印》 中,第 4 小节就介绍了如何自定义 Banner 接口实现。

通过 SpringApplication 提供的 setBanner 方法,我们可以修改入口类,如下:

java 复制代码
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(DemoApplication.class);
        springApplication.setBanner(new CustomBanner());
        springApplication.run(args);
    }
}

1.3 设置应用程序主入口类

在笔者的《初识 SpringApplication》 中,2.6 小节介绍了 SpringApplication 实例化时,会自动推断应用入口类,最终会被赋值给 SpringApplication 的成员变量 mainApplicationClass

当然,通过 SpringApplication 提供的 setMainApplicationClass 方法,设置一个特定的主应用程序类,该类将用作日志源并获取版本信息。默认情况下,SpringApplication 实例化时,会自动推断主应用程序类。如果没有明确的应用程序类,我们可以设置为 null

1.4 设置用于创建应用程序上下文的工厂

java 复制代码
public void setApplicationContextFactory(ApplicationContextFactory applicationContextFactory) {
	this.applicationContextFactory = (applicationContextFactory != null) ? applicationContextFactory
			: ApplicationContextFactory.DEFAULT;
}

通过 SpringApplication 提供的 setApplicationContextFactory 方法,我们可以用于创建应用程序上下文的工厂。如果没有设置,默认为一个工厂【即 DefaultApplicationContextFactory】,该工厂将为 Servlet Web 应用程序 创建 AnnotationConfigServletWebServerApplicationContext,为响应式 Web 应用程序 创建 AnnotationConfigReactiveWebServerApplicationContext,并为 非 Web 应用程序 创建 AnnotationConfigApplicationContext

1.5 添加 BootstrapRegistry 初始化器实现

Huazie《BootstrapRegistryInitializer 详解》中,介绍了 加载和初始化 BootstrapRegistryInitializer 的逻辑,有需要的小伙伴可以去瞅一眼。

那除了默认的加载过程,还有啥办法手动添加 BootstrapRegistryInitializer 呢?

通过 SpringApplicationaddBootstrapRegistryInitializer 方法,我们可以在 bootstrapRegistryInitializers 中添加额外的 BootstrapRegistry 初始化器实现。

java 复制代码
public void addBootstrapRegistryInitializer(BootstrapRegistryInitializer bootstrapRegistryInitializer) {
	Assert.notNull(bootstrapRegistryInitializer, "BootstrapRegistryInitializer must not be null");
	this.bootstrapRegistryInitializers.addAll(Arrays.asList(bootstrapRegistryInitializer));
}

1.6 设置或添加 ApplicationContext 初始化器实现

Huazie《ApplicationContextInitializer 详解》中,介绍了加载和初始化 ApplicationContextInitializer 的逻辑,大家可以自行去回顾下。

除了默认的加载过程,我们还可以通过 SpringApplication 自身进行设置或添加

通过 SpringApplicationsetInitializers 方法,我们可以重新设置 initializers【注意: 调用 setInitializers 方法后,该变量之前的赋值都将丢失】。

java 复制代码
private List<ApplicationContextInitializer<?>> initializers;

public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
	this.initializers = new ArrayList<>(initializers);
}

通过 SpringApplicationaddInitializers 方法,我们可以在 initializers 中添加额外的 ApplicationContextInitializer 数组。

java 复制代码
public void addInitializers(ApplicationContextInitializer<?>... initializers) {
	this.initializers.addAll(Arrays.asList(initializers));
}

1.7 设置 ApplicationListener 实现

Huazie《ApplicationListener 详解》中,我们详细分析了 ApplicationListener 的加载和处理应用程序事件的逻辑,有需要可以去回顾下。

那除了默认的加载流程,我们还可以通过 SpringApplicationsetListeners 方法,重新设置 listeners注意: 调用 setListeners 方法后,listeners 之前的赋值都将丢失】。

java 复制代码
private List<ApplicationListener<?>> listeners;

public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
	this.listeners = new ArrayList<>(listeners);
}

当然也可以通过 SpringApplicationaddListeners 方法,在 listeners 中添加额外的 ApplicationListener 数组。

java 复制代码
public void addListeners(ApplicationListener<?>... listeners) {
	this.listeners.addAll(Arrays.asList(listeners));
}

1.8 设置要运行的Web应用程序的类型

Huazie《初识 SpringApplication》中,2.2 小节分析了 SpringApplication 构造函数中默认的 Web 应用类型推断的逻辑。

当然,我们也可以通过 SpringApplicationsetWebApplicationType 方法,手动设置要运行的 Web 应用程序的类型。

java 复制代码
public void setWebApplicationType(WebApplicationType webApplicationType) {
	Assert.notNull(webApplicationType, "WebApplicationType must not be null");
	this.webApplicationType = webApplicationType;
}

1.9 设置 SpringApplication 中 各种 boolean 变量

1.9.1 setAllowBeanDefinitionOverriding 方法
java 复制代码
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
	this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
}

设置是否允许通过注册一个与现有定义具有相同名称的定义来覆盖 bean 定义。默认为 false

具体可见 DefaultListableBeanFactory#setAllowBeanDefinitionOverriding(boolean)

1.9.2 setAllowCircularReferences 方法
java 复制代码
public void setAllowCircularReferences(boolean allowCircularReferences) {
	this.allowCircularReferences = allowCircularReferences;
}

设置是否允许 bean 之间的循环引用,并自动尝试解析它们。默认为 false

具体可见 AbstractAutowireCapableBeanFactory#setAllowCircularReferences(boolean)

1.9.3 setLazyInitialization 方法
java 复制代码
public void setLazyInitialization(boolean lazyInitialization) {
	this.lazyInitialization = lazyInitialization;
}

设置是否应延迟初始化 beans 。默认为 false

具体可见 BeanDefinition#setLazyInit(boolean)

1.9.4 setHeadless 方法
java 复制代码
public void setHeadless(boolean headless) {
	this.headless = headless;
}

设置应用程序是否为无头模式,即是否不应实例化 AWT 。默认为 true,以防止出现 Java 图标。

1.9.5 setRegisterShutdownHook 方法
java 复制代码
public void setRegisterShutdownHook(boolean registerShutdownHook) {
	this.registerShutdownHook = registerShutdownHook;
}

设置是否应注册一个关闭钩子(shutdown hook)到创建的 ApplicationContext。默认为 true,以确保 JVM 关闭时能够优雅地处理。

1.9.6 setLogStartupInfo 方法
java 复制代码
public void setLogStartupInfo(boolean logStartupInfo) {
	this.logStartupInfo = logStartupInfo;
}

设置在应用程序启动时是否应记录应用程序信息。默认为 true

1.9.7 setAddCommandLineProperties 方法
java 复制代码
public void setAddCommandLineProperties(boolean addCommandLineProperties) {
	this.addCommandLineProperties = addCommandLineProperties;
}

设置是否应将 CommandLinePropertySource 添加到应用程序上下文中,以便暴露参数。默认为 true

1.9.8 setAddConversionService 方法
java 复制代码
public void setAddConversionService(boolean addConversionService) {
	this.addConversionService = addConversionService;
}

设置是否应将 ApplicationConversionService 添加到应用程序上下文的环境中。

Spring Boot 中,ApplicationConversionService 是一个重要的类型转换服务,用于实现应用程序中的数据转换。它提供了一种将一种类型的数据转换为另一种类型数据的方法,使得在不同组件或服务之间能够进行数据交互和集成。

1.10 设置默认的环境属性

java 复制代码
public void setDefaultProperties(Map<String, Object> defaultProperties) {
	this.defaultProperties = defaultProperties;
}

public void setDefaultProperties(Properties defaultProperties) {
	this.defaultProperties = new HashMap<>();
	for (Object key : Collections.list(defaultProperties.propertyNames())) {
		this.defaultProperties.put((String) key, defaultProperties.get(key));
	}
}

上述方法用于设置默认的环境属性,这些属性将在现有环境属性的基础上进行添加。

1.11 设置要使用的额外的配置文件值

java 复制代码
public void setAdditionalProfiles(String... profiles) {
	this.additionalProfiles = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(profiles)));
}

该方法用于设置要使用的额外的配置文件值,这些值将在系统或命令行属性的基础上进行补充。

1.12 设置 bean 名称生成器

java 复制代码
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
	this.beanNameGenerator = beanNameGenerator;
}

设置在生成 bean 名称时应该使用的 bean 名称生成器。

1.13 设置底层环境

java 复制代码
public void setEnvironment(ConfigurableEnvironment environment) {
	this.isCustomEnvironment = true;
	this.environment = environment;
}

设置与创建的应用程序上下文一起使用的底层环境。

1.14 设置资源加载器

java 复制代码
public void setResourceLoader(ResourceLoader resourceLoader) {
	Assert.notNull(resourceLoader, "ResourceLoader must not be null");
	this.resourceLoader = resourceLoader;
}

设置在加载资源时应使用的 ResourceLoader

1.15 设置环境配置属性前缀

java 复制代码
public void setEnvironmentPrefix(String environmentPrefix) {
	this.environmentPrefix = environmentPrefix;
}

设置从系统环境中获取配置属性时应使用的前缀。

1.16 设置应用启动指标对象

java 复制代码
public void setApplicationStartup(ApplicationStartup applicationStartup) {
	this.applicationStartup = (applicationStartup != null) ? applicationStartup : ApplicationStartup.DEFAULT;
}

设置用于收集启动指标的 ApplicationStartup。如果没有指定,则默认使用 DefaultApplicationStartup

2. 数据源配置

除了上述直接通过 setadd 方法来进行参数的配置,SpringApplication 中还提供了可以通过设置配置源参数对整个配置文件或配置类进行配置。

2.1 通过 SpringApplication 构造方法参数

在 Huazie 的《初识 SpringApplication》中的 2.1 小节就介绍了可以通过其构造参数 primarySources 来配置普通类或指定某个配置类,但这种方式有其局限性,它无法指定 XML 配置和基于 package 的配置。

2.2 通过 setSources 方法

先来看看相关的源码:

java 复制代码
private Set<String> sources = new LinkedHashSet<>();

public void setSources(Set<String> sources) {
	Assert.notNull(sources, "Sources must not be null");
	this.sources = new LinkedHashSet<>(sources);
}

该方法的参数为 String 类型的 Set 集合,可以传类名、package 名 和 XML 配置文件资源。

下面我们来演示一下:

  • 首先,我们在 application.properties 中添加如下配置:

    java 复制代码
    author=huazie
  • 然后,新增一个普通类 CustomConfiguration,如下:

    java 复制代码
    public class CustomConfiguration {
    
        @Value("${author}")
        private String author;
    
        public CustomConfiguration() {
            System.out.println("CustomConfiguration已被创建");
        }
    
        public String getAuthor() {
            return author;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    }
  • 接着,我们重新编写 DemoApplication 类,如下所示:

    java 复制代码
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication springApplication = new SpringApplication(DemoApplication.class);
            Set<String> sources = new HashSet<>();
            sources.add(CustomConfiguration.class.getName());
            springApplication.setSources(sources);
            ConfigurableApplicationContext context = springApplication.run(args);
            CustomConfiguration customConfiguration = context.getBean(CustomConfiguration.class);
            System.out.println(customConfiguration.getAuthor());
        }
    }
  • 最后,我们运行 DemoApplication 中的 main 方法,从如下截图中可以看出这里已经打印了自定义类的属性值:

2.3 合并配置源信息

无论是通过构造参数,还是通过 setSources 方法,对配置源信息进行指定,在 Spring Boot 中都会将其合并。

这里我们就不得不提,SpringApplication 提供的 getAllSources 方法,

该方法将构造函数中指定的任何主要源 primarySources 与已显式设置的任何其他源 sources 组合在一起。

四、总结

23 年 7 月Huazie 正式开启了【Spring 源码学习 】系列,一路下来也有 22 篇文章了。24 年该系列将继续更新下去,希望阅读和订阅多多益善!!!

相关推荐
morris13135 分钟前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
阿伟*rui3 小时前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
paopaokaka_luck5 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
Yaml47 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~7 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616887 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
程序媛小果9 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
AskHarries11 小时前
Spring Boot集成Access DB实现数据导入和解析
java·spring boot·后端
2401_8576226611 小时前
SpringBoot健身房管理:敏捷与自动化
spring boot·后端·自动化
程序员阿龙11 小时前
基于SpringBoot的医疗陪护系统设计与实现(源码+定制+开发)
java·spring boot·后端·医疗陪护管理平台·患者护理服务平台·医疗信息管理系统·患者陪护服务平台