首頁  >  文章  >  Java  >  SpringBoot外部化如何配置

SpringBoot外部化如何配置

WBOY
WBOY轉載
2023-05-15 09:43:051199瀏覽

SpringBoot外部化配置(基於2.4.0以後)

Spring Boot可以讓你將設定外部化,這樣你就可以在不同的環境中使用相同的應用程式程式碼。你可以使用各種外部設定來源,包括Java properties 檔案、YAML檔案、環境變數和命令列參數。

屬性值可以透過使用 @Value 註解直接注入你的Bean,也可以透過Spring 的 Environment 訪問,或透過 @ConfigurationProperties 綁定到物件。同時 Spring Boot 也提供了一個非常特殊的 PropertyOrder,來允許使用者可以在適當的場景下覆蓋某些屬性值,該順序旨在允許合理地覆蓋值。

依下列順序優先權從低到高, 後者的屬性值覆寫前者,所有的設定會形成互補配置:

預設屬性(使用SpringApplication.setDefaultProperties 指定)

@Configuration類別上的@PropertySource註解引入的設定屬性

請注意,這樣的屬性來源直到ApplicationContext被刷新時才會被加入到環境中。這對於配置某些屬性來說已經太晚了,例如logging.* spring.main.* ,它們在刷新開始前就已經被讀取了。

配置資料(例如application.properties檔案)

對於random.*形式的屬性,優先從RandomValuePropertySource取得(指優先於後者)

OS environment variables((作業系統環境變數)

Java System properties(Java 系統屬性System.getProperties()

JNDI屬性

ServletContext 的初始化參數

ServletConfig 的初始化參數

SPRING_APPLICATION_JSON 屬性

#命令列參數

#test 模組下的properties 屬性

test 模組下@TestPropertySource 註解引入的設定檔

啟用devtools 時$HOME/.config/spring-boot 路徑下的設定

#設定資料檔按以下載入順序考慮:

  • 打包在jar 中的應用程式屬性(application.properties 和YAML)

  • 打包在jar 中的特定設定文件的應用程式屬性(application-{profile}.properties 和YAML)

  • #打包jar 以外的應用程式屬性(application.properties 和YAML)

  • #打包jar 以外的特定設定檔的應用程式屬性(application-{profile}.properties 和YAML)

SpringBoot設定檔

Spring中常見的設定檔類型

  • XML資源

  • #Properties資源

  • ##YAML資源

Profile概述

Profile 本質上代表一種用於組織配置資訊的維度,在不同場景下可以代表不同的意義。例如,如果Profile 代表的是一種狀態,我們可以使用open、halfopen、close 等值來分別代表全開、半開和關閉等。再例如係統需要設定一系列的模板,每個模板中​​保存著一系列配置項目。

設定命名規則:

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

設定檔載入順序

Spring Boot 啟動時,會自動載入JAR 套件內部及JAR 套件所在目錄指定位置的設定檔(Properties文件、YAML 文件)。清單會依優先排序(較低項目的值覆寫較早項目的值)

classpath( –classpath )

classpath 根路徑

classpath 下的/config套件

目前目錄( –file )

目前目錄下

#目前目錄下的config/ 子目錄

目前目錄下的config/ 子目錄的直接子目錄

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

啟動時載入設定檔順序:1 > 2 > 3 > 4

Profile 設定覆蓋變更(2.4.0以後)

2.4.0先前版本,預設情況的載入順序如下:

  • 打包在jar 中的應用程式屬性(application.properties 和YAML)。

  • 打包jar 以外的應用程式屬性(application.properties 和YAML)

  • 打包在jar 中的特定於設定檔的應用程式屬性(application-{profile}.properties 和YAML)

  • #打包jar 之外的特定於設定檔的應用程式屬性(application-{profile}.properties 和YAML)

注意:在先前的版本中,JAR 套件外部的

application.properties設定檔不會覆蓋JAR 套件裡面的基於"profile" 的application -{profile}.properties 設定檔。

2.4.0以後版本,預設情況的搜尋順序如下:保證了JAR 套件外部的應用程式參數應優先於JAR 套件內部的特定啟動的設定參數

  • 打包在jar 中的應用程式屬性(application.properties 和YAML)。

  • 打包在jar 中的特定於設定檔的應用程式屬性(application-{profile}.properties 和YAML)

  • ##打包jar之外的應用程式屬性(application.properties 和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对象可以包含下表中描述的属性。

##String##name
Name 類型
#name
##String #屬性的全名。名稱採用小寫的句號分隔形式(例如,server.address)。這個屬性是強制性的。
type String 該屬性的資料類型的完整簽名(例如,java.lang.String),但也有完整的通用類型(例如java.util.Map)。您可以使用此屬性來指導使用者可以輸入的值的類型。為了保持一致性,基元的類型是透過使用其包裝類型來指定的(例如,boolean 變成 java.lang.Boolean)。如果該類型不知道,可以省略。
description String 可以顯示給使用者的該property的簡短描述。如果沒有描述,可以省略。描述中的最後一行應以句號(.)結束。
sourceType String 貢獻此屬性的來源的類別名稱。例如,如果該屬性來自於一個用 @ConfigurationProperties 註解的類,則該屬性將包含該類別的完全限定名稱。如果來源類型未知,可以省略。
defaultValue Object 預設值,如果沒有指定屬性,則使用該值。如果該屬性的類型是數組,它可以是數組的值。如果預設值是未知的,它可以被省略。
deprecation
Deprecation

指定該屬性是否被廢棄。如果該欄位沒有被廢棄,或不知道該訊息,可以省略。下表提供了關於 deprecation 屬性的更多細節。 Hint 屬性包含在 hints 陣列中的JSON物件可以包含下表中的屬性。
Name 類型
name #String 此提示所指向的屬性的全名。名稱採用小寫的句號分隔形式(如 spring.mvc.servlet.path)。這個屬性是強制性的。
values

ValueHint[]#由 ValueHint 物件定義的有效值的清單(在下表中描述)。每個條目都定義了值,可以有一個description。 每個 hint 元素的 values 屬性中包含的JSON物件可以包含下表中所述的屬性。 ##valueObject 提示所指的元素的一個有效值。如果該屬性的類型是數組,它也可以是數組的值。這個屬性是強制性的。 description
Name 類型 目的
###String######可以顯示給使用者的價值的簡短描述。如果沒有描述,可以省略。描述中的最後一行應以句號(.)結束。 ############

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包之前。

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

SpringBoot外部化如何配置

SpringBoot外部化如何配置

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.

  • AvailabilityChangeEvent 在 ReadinessState.ACCEPTING_TRAFFIC 之後立即發送,以指示應用程式已準備好服務請求。

  • 如果存在以下情況,則會發送 ApplicationFailedEvent啟動啟動時出現異常。

上面的清單僅包含與SpringApplication 綁定的SpringApplicationEvents。除此之外,以下事件也會在 ApplicationPreparedEvent 之後和 ApplicationStartedEvent 之前發布:

  • WebServer 準備好後發送 WebServerInitializedEvent。 ServletWebServerInitializedEvent 和 ReactiveWebServerInitializedEvent 分別是 servlet 和響應式變體。

  • 刷新 ApplicationContext 時發送 ContextRefreshedEvent。

刷新 ApplicationContext 時發送 ContextRefreshedEvent。#########

以上是SpringBoot外部化如何配置的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除