Home  >  Article  >  Java  >  How to configure SpringBoot externalization

How to configure SpringBoot externalization

WBOY
WBOYforward
2023-05-15 09:43:051199browse

SpringBoot externalized configuration (based on 2.4.0 and later)

Spring Boot allows you to externalize the configuration so that you can use the same application code in different environments. You can use a variety of external configuration sources, including Java properties files, YAML files, environment variables, and command line parameters.

Property values ​​can be injected directly into your beans using the @Value annotation, accessed through Spring's Environment, or bound to objects through @ConfigurationProperties. At the same time, Spring Boot also provides a very special PropertyOrder to allow users to override certain property values ​​in appropriate scenarios. This order is designed to allow reasonable overriding of values.

The priority is from low to high in the following order. The property value of the latter overrides the former. All configurations will form complementary configurations:

Default properties (specified using SpringApplication.setDefaultProperties)

@ConfigurationThe configuration property introduced by the @PropertySource annotation on the class

Please note that such a property source will not be refreshed until ApplicationContext will be added to the environment. This is too late to configure some properties, such as logging.* and spring.main.* , which have been read before the flush has started.

Configuration data (such as application.properties file)

For properties in the form of random.*, they are obtained from RandomValuePropertySource first (referring to priority over )

OS environment variables((operating system environment variables)

Java System properties(Java system propertiesSystem.getProperties())

JNDI Properties

Initialization parameters of ServletContext

Initialization parameters of ServletConfig

SPRING_APPLICATION_JSON properties

Command line parameters

properties properties under the test module

The configuration file introduced by the @TestPropertySource annotation under the test module

The configuration under the $HOME/.config/spring-boot path when devtools is enabled

The configuration data file is loaded as follows Sequence to consider:

  • Application properties (application.properties and YAML) packaged in the jar

  • Specific configuration files packaged in the jar Application properties (application-{profile}.properties and YAML)

  • Application properties (application.properties and YAML) outside the packaged jar

  • Application properties (application-{profile}.properties and YAML) for specific profiles outside of packaged jar

SpringBoot configuration file

In Spring Common configuration file types

  • XML resources

  • Properties resources

  • YAML resources

Profile Overview

Profile essentially represents a dimension used to organize configuration information, and can represent different meanings in different scenarios. For example, if Profile represents a Status, we can use open, halfopen, close and other values ​​to represent fully open, half open and closed respectively. For another example, the system needs to set up a series of templates, and each template saves a series of configuration items.

Configuration naming rules:

/{application}.yml
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

Configuration file loading sequence

When Spring Boot starts, it will automatically load the configuration file (Properties) inside the JAR package and at the specified location in the directory where the JAR package is located. files, YAML files). The list is sorted by priority (values ​​for lower items override values ​​for earlier items)

classpath ( –classpath )

classpath root path

/config under classpath Package

Current directory (–file)

Under the current directory

The config/ subdirectory under the current directory

The config/ subdirectory under the current directory Direct subdirectories of the directory

. project-sample
├── config
│   ├── application.yml (4)
│   └── src/main/resources
|   │   ├── application.yml (1)
|   │   └── config
|   |   │   ├── application.yml (2)
├── application.yml (3)

Sequence of loading configuration files at startup: 1 > 2 > 3 > 4

Profile configuration coverage changes (after 2.4.0)

2.4.0Previous versions, the default loading order was as follows:

  • Application properties (application.properties and YAML) packaged in jar.

  • Packaging application properties (application.properties and YAML) outside the jar

  • Profile-specific applications packaged in a jar Application properties (application-{profile}.properties and YAML)

  • Profile-specific application properties (application-{profile}.properties and YAML)# outside of the packaged jar

Note: In previous versions, the application.properties configuration file outside the JAR package would not overwrite the "profile"-based application inside the JAR package. -{profile}.properties Configuration file.

2.4.0In later versions, the default search order is as follows: ensuring that application parameters outside the JAR package should take precedence over specific activation configuration parameters inside the JAR package

  • Application properties (application.properties and YAML) packaged in the jar.

  • Profile-specific application properties packaged in the jar (application-{profile}.properties and YAML)

  • Packaging jar Application properties outside of application.properties and YAML

  • 打包 jar 之外的特定于配置文件的应用程序属性(application-{profile}.properties 和 YAML)

注意:同一位置下,Properties 文件优先级高于 YAML 文件 , 如果Spring Boot在优先级更高的位置找到了配置,那么它就会无视优先级低的配置。

文档排序(2.4.0以后)

从 Spring Boot 2.4 开始,加载 Properties 和 YAML 文件时候会遵循, 在文档中声明排序靠前的属性将被靠后的属性覆盖 。

激活指定配置文件

命令行激活: --spring.profiles.active=prod 

spring:
  profiles:
    active: dev #激活开发环境配置

配置文件激活如上,只需要在application.yml或者properties文件中配置即可

注意:在application.yml或者properties文件存在的情况下,不管激活的是prod还是dev,还是会读取默认的配置文件,只不过指定的配置文件会覆盖默认配置文件中的属性

导入额外的配置文件(2.4.0以后)

可以使用spring.config.import属性从其他地方导入更多的配置数据,比如spring.config.import=my.yaml 。它会将 my.yaml 文件作为临时文件放在当前配置文件之后处理,因此其属性具有更高的优先级

激活外部配置文件

在运行Jar包的命令中加入这个参数就可以指定Jar包以外的配置文件的位置了,也可以在application的配置文件中配置该属性

$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

这个参数就是指定外部application.yml配置文件位置的参数,它支持classpathfile路径

java -jar myproject.jar --spring.config.name=myproject

如果您不喜欢application.properties作为配置文件名,您可以通过指定spring.config.name环境属性来切换到另一个文件名

optional可选的配置文件

对于spring.config.locationspring.config.additional-locationspring.config.import等属性的路径,添加optional:前缀,则当对应文件不存在时应用仍可以正常启动

比如spring.config.location=optional:file:/my.yaml,当应用启动加载文件 my.yaml 不存在时,不会抛出异常

嵌入系统配置信息

例如,如果想要获取当前应用程序的名称并作为一个配置项进行管理,那么很简单,我们直接通过 ${spring.application.name} 占位符:

myapplication.name : ${spring.application.name}

假设我们使用 Maven 来构建应用程序,那么可以按如下所示的配置项来动态获取与系统构建过程相关的信息:

info: 
  app:
    encoding: @project.build.sourceEncoding@
    java:
      source: @java.version@
      target: @java.version@
# 等同于下述效果
info:
  app:
    encoding: UTF-8
    java:
        source: 1.8.0_31
        target: 1.8.0_31

配置参数提示

additional-spring-configuration-metadata.jsonspring-configuration-metadata.json在springboot-starter官方项目或第三方starter项目中随处可见,那它起的作用是什么?

  • 配置additional-spring-configuration-metadata.json文件后,在开发人员的IDE工具使用个人编写的配置读取很有效的在application.propertiesapplication.yml文件下完成提示

配置处理器

在Maven中,该依赖关系应被声明为可选的,如以下例子所示。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

创建additional-spring-configuration-metadata.json

resources/META-INF目录下创建additional-spring-configuration-metadata.json,分类为 “groups” 或 “properties”,附加值提示分类为 "hints",如以下例子所示:

{
    "groups": [
        {
            "name": "server",
            "type": "org.springframework.boot.autoconfigure.web.ServerProperties",
            "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
        },
        {
            "name": "spring.jpa.hibernate",
            "type": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate",
            "sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties",
            "sourceMethod": "getHibernate()"
        }
    ...
    ],
    "properties": [
        {
            "name": "server.port",
            "type": "java.lang.Integer",
            "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
        },
        {
            "name": "server.address",
            "type": "java.net.InetAddress",
            "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
        },
        {
            "name": "spring.jpa.hibernate.ddl-auto",
            "type": "java.lang.String",
            "description": "DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property.",
            "sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate"
        }
    ...
    ],
    "hints": [
        {
            "name": "spring.jpa.hibernate.ddl-auto",
            "values": [
                {
                    "value": "none",
                    "description": "Disable DDL handling."
                },
                {
                    "value": "validate",
                    "description": "Validate the schema, make no changes to the database."
                },
                {
                    "value": "update",
                    "description": "Update the schema if necessary."
                },
                {
                    "value": "create",
                    "description": "Create the schema and destroy previous data."
                },
                {
                    "value": "create-drop",
                    "description": "Create and then destroy the schema at the end of the session."
                }
            ]
        }
    ]
}
Property 属性

properties 数组中包含的JSON对象可以包含下表中描述的属性。

Name Type Purpose
name String The full name of the attribute. The name is lowercase, period-separated (for example, server.address). This attribute is mandatory.
type String The full signature of the property's data type (for example, java.lang.String), but there are also full generic types (For example, java.util.Map). You can use this property to instruct users on the types of values ​​they can enter. For consistency, the type of a primitive is specified by using its wrapper type (for example, boolean becomes java.lang.Boolean). If the type is not known, it can be omitted.
description String A short description of the property that can be displayed to the user. If there is no description, it can be omitted. The last line in the description should end with a period (.).
sourceType String The class name of the source that contributed this property. For example, if the property comes from a class annotated with @ConfigurationProperties, the property will contain the fully qualified name of the class. Can be omitted if the source type is unknown.
defaultValue Object Default value, if this property is not specified, this value is used. If the property's type is an array, it can be an array of values. If the default value is unknown, it can be omitted.
deprecation Deprecation Specifies whether the attribute is deprecated. If this field is not obsolete, or this information is not known, it can be omitted. The following table provides more details about the deprecation attribute.
Hint properties

The JSON object contained in the hints array can contain the properties in the following table.

Name Type Purpose
name String The full name of the property pointed to by this tip. The name is in lowercase, period-separated form (such as spring.mvc.servlet.path). This attribute is mandatory.
values ValueHint[] The list of valid values ​​defined by the ValueHint object (described in the following table). Each entry has a defined value and can have a description.

The JSON object contained in the values ​​attribute of each hint element can contain the properties described in the following table.

Name Type Purpose
value Object A valid value for the element pointed to by the hint. If the property's type is an array, it can also be an array value. This attribute is mandatory.
description String A short description of the value that can be displayed to the user. If there is no description, it can be omitted. The last line in the description should end with a period (.).

SpringBoot命令行参数

参考:https://www.yisu.com/article/191629.htm

启动Spring Boot项目时传递参数,有三种参数形式:

  • 选项参数,基本格式为--optName[=optValue]--为连续两个减号)

--foo
--foo=bar
--foo="bar then baz"
--foo=bar,baz,biz
  • 非选项参数

java -jar xxx.jar abc def

  • 系统参数

java -jar -Dserver.port=8081 xxx.jar

相当于 SpringBoot 基于 Java 命令行参数中的非选项参数自定义了选项参数的规则,具体可以看解析器SimpleCommandLineArgsParser,它里面调用其parse方法对参数进行解析

class SimpleCommandLineArgsParser {
    public CommandLineArgs parse(String... args) {
        CommandLineArgs commandLineArgs = new CommandLineArgs();
        for (String arg : args) {
            // --开头的选参数解析
            if (arg.startsWith("--")) {
                // 获得key=value或key值
                String optionText = arg.substring(2, arg.length());
                String optionName;
                String optionValue = null;
                // 如果是key=value格式则进行解析
                if (optionText.contains("=")) {
                    optionName = optionText.substring(0, optionText.indexOf(&#39;=&#39;));
                    optionValue = optionText.substring(optionText.indexOf(&#39;=&#39;)+1, optionText.length());
                } else {
                    // 如果是仅有key(--foo)则获取其值
                    optionName = optionText;
                }
                // 如果optionName为空或者optionValue不为空但optionName为空则抛出异常
                if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
                    throw new IllegalArgumentException("Invalid argument syntax: " + arg);
                }
                // 封装入CommandLineArgs
                commandLineArgs.addOptionArg(optionName, optionValue);
            } else {
                commandLineArgs.addNonOptionArg(arg);
            }
        }
        return commandLineArgs;
    }
}

参数值的获取

如果您需要访问传递给应用程序的参数SpringApplication.run(…),您可以注入一个ApplicationArguments。该ApplicationArguments接口提供对原始String[]参数以及选项参数和非选项参数的访问,如以下示例所示:

@Component
public class MyBean {
    @Autowired
    public MyBean(ApplicationArguments args) {
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
        // if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
    }
}
  • 另外,选项参数,也可以直接通过@Value在类中获取

  • 系统参数可以通过java.lang.System提供的方法获取

参数值的区别

关于参数值区别,重点看选项参数和系统参数。通过上面的示例我们已经发现使用选项参数时,参数在命令中是位于xxx.jar之后传递的,而系统参数是紧随java -jar之后。

如果不按照该顺序进行执行,比如使用如下方式使用选项参数:

java -jar --server.port=8081 xxx.jar

则会抛出如下异常:

Unrecognized option: --server.port=8081
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

如果将系统参数放在jar包后面,问题会更严重,会出现可以正常启动,但参数无法生效。这个错误是最坑的,所以一定谨记:通过-D传递系统参数时,务必放置在待执行的jar包之前。

扩展“外部化配置”属性源

How to configure SpringBoot externalization

How to configure SpringBoot externalization

SpingBoot怎么支持YAML配置文件解析?

处理@PropertySource注解从ConfigurationClassParser#processPropertySource方法进

Spring中@PropertySource默认不支持YAML格式的解析,但是SpringBoot的配置文件却可以解析YAML,这说明SpringBoot中已经实现了YAML文件的解析,我们只需要复用即可,我们可以看该注解源码

/**
 * Specify a custom {@link PropertySourceFactory}, if any.
 * <p>By default, a default factory for standard resource files will be used.
 * @since 4.3
 * @see org.springframework.core.io.support.DefaultPropertySourceFactory
 * @see org.springframework.core.io.support.ResourcePropertySource
 */
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;

PropertySourceFactory的默认实现是DefaultPropertySourceFactory

public class DefaultPropertySourceFactory implements PropertySourceFactory {
	@Override
	public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
		return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
	}
}

ResourcePropertySource默认不支持YAML,所以我们可以通过实现PropertySourceFactory接口,然后用@PropertySource的factory属性来实现YAML的解析

public class YamlPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
        yamlPropertiesFactoryBean.setResources(resource.getResource());
        Properties yamlProperties = yamlPropertiesFactoryBean.getObject();
        return new PropertiesPropertySource(name, yamlProperties);
    }
}

关于ApplicationEnvironmentPreparedEvent没有被执行的原因

官方文档中有说到:有些事件实际上是在ApplicationContext被创建之前触发的,所以我们不能将这些事件的监听器注册为@Bean。

因为这个时候应用上下文还没有被创建,也就是说监听器也还没有被初始化,这个先后顺序不对,会导致这些事件的监听器不会被触发

但可以使用SpringApplication.addListeners(...) 方法或SpringApplicationBuilder.listeners(...) 方法注册它们。

如果您希望这些侦听器自动注册的话,可以通过新建一个META-INF/spring.factories文件,添加类似以下内容,SpringBoot会自动帮你注册。

org.springframework.context.ApplicationListener=com.example.project.MyListener
应用程序事件

应用程序运行时,应用程序事件按以下顺序发送:

  • An ApplicationStartingEvent is sent at the start of a run but before any processing, except for the registration of listeners and initializers.

  • An ApplicationEnvironmentPreparedEvent is sent when the Environment to be used in the context is known but before the context is created.

  • An ApplicationContextInitializedEvent is sent when the ApplicationContext is prepared and ApplicationContextInitializers have been called but before any bean definitions are loaded.

  • An ApplicationPreparedEvent is sent just before the refresh is started but after bean definitions have been loaded.

  • An ApplicationStartedEvent is sent after the context has been refreshed but before any application and command-line runners have been called.

  • An AvailabilityChangeEvent is sent right after with LivenessState.CORRECT to indicate that the application is considered as live.

  • An ApplicationReadyEvent is sent after any application and command-line runners have been called.

  • An AvailabilityChangeEvent is sent right after with ReadinessState.ACCEPTING_TRAFFIC to indicate that the application is ready to service requests.

  • An ApplicationFailedEvent is sent if there is an exception on startup.

The above list only includes SpringApplicationEvents that are tied to a SpringApplication. In addition to these, the following events are also published after ApplicationPreparedEvent and before ApplicationStartedEvent:

  • A WebServerInitializedEvent is sent after the WebServer is ready. ServletWebServerInitializedEvent and ReactiveWebServerInitializedEvent are the servlet and reactive variants respectively.

  • A ContextRefreshedEvent is sent when an ApplicationContext is refreshed.

The above is the detailed content of How to configure SpringBoot externalization. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete