前面的博文,Huazie 带大家从 Spring Boot 的启动类 SpringApplication
上入手,了解了 SpringApplication
的实例化过程。这实例化构造过程中包含了各种初始化的操作,都是 Spring Boot 默认配置的。如果我们需要定制化配置,SpringApplication
也提供了相关的入口,且看下面的介绍。
在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:
针对 SpringApplication
的定制化配置,Spring Boot 中也提供了不同的方式,比如通过入口类、配置文件、环境变量、命令行参数等等。
注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。
所谓基础配置,即是可以直接通过 set
或 add
方法来进行参数的配置,这些 set
或 add
方法修改的配置都是 Spring Boot 预定义的一些参数,有些也可以在 application.properties 文件中进行配置。
在笔者的《Banner 信息打印流程》 中,第 2 小节就介绍了如何关闭 Banner 信息打印。
通过 SpringApplication
提供的 setBannerMode
方法,我们就可以在启动入口类中,这样来编写:
@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
方法,我们可以修改入口类,如下:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(DemoApplication.class);
springApplication.setBanner(new CustomBanner());
springApplication.run(args);
}
}
在笔者的《初识 SpringApplication》 中,2.6 小节介绍了 SpringApplication
实例化时,会自动推断应用入口类,最终会被赋值给 SpringApplication
的成员变量 mainApplicationClass
。
当然,通过 SpringApplication
提供的 setMainApplicationClass
方法,设置一个特定的主应用程序类,该类将用作日志源并获取版本信息。默认情况下,SpringApplication
实例化时,会自动推断主应用程序类。如果没有明确的应用程序类,我们可以设置为 null。
public void setApplicationContextFactory(ApplicationContextFactory applicationContextFactory) {
this.applicationContextFactory = (applicationContextFactory != null) ? applicationContextFactory
: ApplicationContextFactory.DEFAULT;
}
通过 SpringApplication
提供的 setApplicationContextFactory
方法,我们可以用于创建应用程序上下文的工厂。如果没有设置,默认为一个工厂【即 DefaultApplicationContextFactory
】,该工厂将为 Servlet Web 应用程序 创建 AnnotationConfigServletWebServerApplicationContext
,为响应式 Web 应用程序 创建 AnnotationConfigReactiveWebServerApplicationContext
,并为 非 Web 应用程序 创建 AnnotationConfigApplicationContext
。
在 Huazie 的《BootstrapRegistryInitializer 详解》中,介绍了 加载和初始化 BootstrapRegistryInitializer
的逻辑,有需要的小伙伴可以去瞅一眼。
那除了默认的加载过程,还有啥办法手动添加 BootstrapRegistryInitializer
呢?
通过 SpringApplication
的 addBootstrapRegistryInitializer
方法,我们可以在 bootstrapRegistryInitializers
中添加额外的 BootstrapRegistry
初始化器实现。
public void addBootstrapRegistryInitializer(BootstrapRegistryInitializer bootstrapRegistryInitializer) {
Assert.notNull(bootstrapRegistryInitializer, "BootstrapRegistryInitializer must not be null");
this.bootstrapRegistryInitializers.addAll(Arrays.asList(bootstrapRegistryInitializer));
}
在 Huazie 的《ApplicationContextInitializer 详解》中,介绍了加载和初始化 ApplicationContextInitializer
的逻辑,大家可以自行去回顾下。
除了默认的加载过程,我们还可以通过 SpringApplication
自身进行设置或添加
通过 SpringApplication
的 setInitializers
方法,我们可以重新设置 initializers【注意: 调用 setInitializers
方法后,该变量之前的赋值都将丢失】。
private List<ApplicationContextInitializer<?>> initializers;
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
}
通过 SpringApplication
的 addInitializers
方法,我们可以在 initializers
中添加额外的 ApplicationContextInitializer
数组。
public void addInitializers(ApplicationContextInitializer<?>... initializers) {
this.initializers.addAll(Arrays.asList(initializers));
}
在 Huazie 的《ApplicationListener 详解》中,我们详细分析了 ApplicationListener 的加载和处理应用程序事件的逻辑,有需要可以去回顾下。
那除了默认的加载流程,我们还可以通过 SpringApplication
的 setListeners
方法,重新设置 listeners
【注意: 调用 setListeners
方法后,listeners
之前的赋值都将丢失】。
private List<ApplicationListener<?>> listeners;
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
当然也可以通过 SpringApplication
的 addListeners
方法,在 listeners
中添加额外的 ApplicationListener
数组。
public void addListeners(ApplicationListener<?>... listeners) {
this.listeners.addAll(Arrays.asList(listeners));
}
在 Huazie 的《初识 SpringApplication》中,2.2 小节分析了 SpringApplication
构造函数中默认的 Web 应用类型推断的逻辑。
当然,我们也可以通过 SpringApplication
的 setWebApplicationType
方法,手动设置要运行的 Web 应用程序的类型。
public void setWebApplicationType(WebApplicationType webApplicationType) {
Assert.notNull(webApplicationType, "WebApplicationType must not be null");
this.webApplicationType = webApplicationType;
}
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
}
设置是否允许通过注册一个与现有定义具有相同名称的定义来覆盖 bean 定义。默认为 false
。
具体可见 DefaultListableBeanFactory#setAllowBeanDefinitionOverriding(boolean)
public void setAllowCircularReferences(boolean allowCircularReferences) {
this.allowCircularReferences = allowCircularReferences;
}
设置是否允许 bean 之间的循环引用,并自动尝试解析它们。默认为 false
。
具体可见 AbstractAutowireCapableBeanFactory#setAllowCircularReferences(boolean)
public void setLazyInitialization(boolean lazyInitialization) {
this.lazyInitialization = lazyInitialization;
}
设置是否应延迟初始化 beans。默认为 false
。
具体可见 BeanDefinition#setLazyInit(boolean)
public void setHeadless(boolean headless) {
this.headless = headless;
}
设置应用程序是否为无头模式,即是否不应实例化 AWT。默认为 true
,以防止出现 Java 图标。
public void setRegisterShutdownHook(boolean registerShutdownHook) {
this.registerShutdownHook = registerShutdownHook;
}
设置是否应注册一个关闭钩子(shutdown hook)到创建的 ApplicationContext
。默认为 true
,以确保 JVM 关闭时能够优雅地处理。
public void setLogStartupInfo(boolean logStartupInfo) {
this.logStartupInfo = logStartupInfo;
}
设置在应用程序启动时是否应记录应用程序信息。默认为 true
。
public void setAddCommandLineProperties(boolean addCommandLineProperties) {
this.addCommandLineProperties = addCommandLineProperties;
}
设置是否应将 CommandLinePropertySource
添加到应用程序上下文中,以便暴露参数。默认为 true
。
public void setAddConversionService(boolean addConversionService) {
this.addConversionService = addConversionService;
}
设置是否应将 ApplicationConversionService
添加到应用程序上下文的环境中。
在 Spring Boot 中,ApplicationConversionService
是一个重要的类型转换服务,用于实现应用程序中的数据转换。它提供了一种将一种类型的数据转换为另一种类型数据的方法,使得在不同组件或服务之间能够进行数据交互和集成。
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));
}
}
上述方法用于设置默认的环境属性,这些属性将在现有环境属性的基础上进行添加。
public void setAdditionalProfiles(String... profiles) {
this.additionalProfiles = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(profiles)));
}
该方法用于设置要使用的额外的配置文件值,这些值将在系统或命令行属性的基础上进行补充。
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
this.beanNameGenerator = beanNameGenerator;
}
设置在生成 bean 名称时应该使用的 bean 名称生成器。
public void setEnvironment(ConfigurableEnvironment environment) {
this.isCustomEnvironment = true;
this.environment = environment;
}
设置与创建的应用程序上下文一起使用的底层环境。
public void setResourceLoader(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
设置在加载资源时应使用的 ResourceLoader
。
public void setEnvironmentPrefix(String environmentPrefix) {
this.environmentPrefix = environmentPrefix;
}
设置从系统环境中获取配置属性时应使用的前缀。
public void setApplicationStartup(ApplicationStartup applicationStartup) {
this.applicationStartup = (applicationStartup != null) ? applicationStartup : ApplicationStartup.DEFAULT;
}
设置用于收集启动指标的 ApplicationStartup
。如果没有指定,则默认使用 DefaultApplicationStartup
。
除了上述直接通过 set
或 add
方法来进行参数的配置,SpringApplication
中还提供了可以通过设置配置源参数对整个配置文件或配置类进行配置。
在 Huazie 的《初识 SpringApplication》中的 2.1 小节就介绍了可以通过其构造参数 primarySources
来配置普通类或指定某个配置类,但这种方式有其局限性,它无法指定 XML 配置和基于 package 的配置。
先来看看相关的源码:
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
中添加如下配置:
author=huazie
然后,新增一个普通类 CustomConfiguration
,如下:
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
类,如下所示:
@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
方法,从如下截图中可以看出这里已经打印了自定义类的属性值:
无论是通过构造参数,还是通过 setSources
方法,对配置源信息进行指定,在 Spring Boot 中都会将其合并。
这里我们就不得不提,SpringApplication
提供的 getAllSources
方法,
该方法将构造函数中指定的任何主要源 primarySources
与已显式设置的任何其他源 sources
组合在一起。
23 年 7 月 ,Huazie 正式开启了【Spring 源码学习】系列,一路下来也有 22 篇文章了。24 年该系列将继续更新下去,希望阅读和订阅多多益善!!!