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)
XML資源
#Properties資源
/{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 > 4Profile 設定覆蓋變更(2.4.0以後)
2.4.0先前版本,預設情況的載入順序如下:
application.properties設定檔不會覆蓋JAR 套件裡面的基於"profile" 的
application -{profile}.properties 設定檔。
2.4.0以後版本,預設情況的搜尋順序如下:保證了JAR 套件外部的應用程式參數應優先於JAR 套件內部的特定啟動的設定參數
打包 jar 之外的特定于配置文件的应用程序属性(application-{profile}.properties 和 YAML)
注意:同一位置下,Properties 文件优先级高于 YAML 文件 , 如果Spring Boot在优先级更高的位置找到了配置,那么它就会无视优先级低的配置。
从 Spring Boot 2.4 开始,加载 Properties 和 YAML 文件时候会遵循, 在文档中声明排序靠前的属性将被靠后的属性覆盖 。
命令行激活: --spring.profiles.active=prod
spring: profiles: active: dev #激活开发环境配置
配置文件激活如上,只需要在application.yml或者properties文件中配置即可
注意:在application.yml或者properties文件存在的情况下,不管激活的是prod还是dev,还是会读取默认的配置文件,只不过指定的配置文件会覆盖默认配置文件中的属性
可以使用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
配置文件位置的参数,它支持classpath
和file
路径
java -jar myproject.jar --spring.config.name=myproject
如果您不喜欢application.properties
作为配置文件名,您可以通过指定spring.config.name
环境属性来切换到另一个文件名
对于spring.config.location
、spring.config.additional-location
和spring.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.json
、spring-configuration-metadata.json
在springboot-starter官方项目或第三方starter项目中随处可见,那它起的作用是什么?
配置additional-spring-configuration-metadata.json
文件后,在开发人员的IDE工具使用个人编写的配置读取很有效的在application.properties
或application.yml
文件下完成提示
在Maven中,该依赖关系应被声明为可选的,如以下例子所示。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
在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." } ] } ] }
properties 数组中包含的JSON对象可以包含下表中描述的属性。
Name | 類型 | 的 |
---|---|---|
#name | ##String||
##name | ##String | #屬性的全名。名稱採用小寫的句號分隔形式(例如,server.address)。這個屬性是強制性的。 |
type | String | 該屬性的資料類型的完整簽名(例如,java.lang.String),但也有完整的通用類型(例如java.util.Map |
description | String | 可以顯示給使用者的該property的簡短描述。如果沒有描述,可以省略。描述中的最後一行應以句號(.)結束。 |
sourceType | String | 貢獻此屬性的來源的類別名稱。例如,如果該屬性來自於一個用 @ConfigurationProperties 註解的類,則該屬性將包含該類別的完全限定名稱。如果來源類型未知,可以省略。 |
defaultValue | Object | 預設值,如果沒有指定屬性,則使用該值。如果該屬性的類型是數組,它可以是數組的值。如果預設值是未知的,它可以被省略。 |
Hint 屬性 | 包含在 hints 陣列中的JSON物件可以包含下表中的屬性。 | |
---|---|---|
Name | 類型 | |
name | #String | 此提示所指向的屬性的全名。名稱採用小寫的句號分隔形式(如 spring.mvc.servlet.path)。這個屬性是強制性的。 |
每個 hint 元素的 values 屬性中包含的JSON物件可以包含下表中所述的屬性。 | ||
---|---|---|
Name | 類型 | 目的 |
Object | 提示所指的元素的一個有效值。如果該屬性的類型是數組,它也可以是數組的值。這個屬性是強制性的。 |
参考: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('=')); optionValue = optionText.substring(optionText.indexOf('=')+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包之前。
处理@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); } }
官方文档中有说到:有些事件实际上是在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。
以上是SpringBoot外部化如何配置的詳細內容。更多資訊請關注PHP中文網其他相關文章!