清理 Spring Properties 文件
1.概述
在為 Spring 專案建立配置屬性時,我們可能會選擇將它們拆分到多個文件中。通常,每個 Spring 設定檔都包含不同的屬性。
隨著時間的推移,這些文件會累積大量的屬性,包含大量重複數據,難以閱讀。清理這些文件需要大量的手動工作。
在這個簡短的教學中,我們將介紹一個名為Spring Properties Cleaner的 Maven 插件,它可以幫助整理和控制一切。
2.範例
2.1. 一些屬性文件
假設我們有兩個設定檔—— dev
和prod
。在這個例子中,我們還沒有建立一個簡單的application.properties
檔案。我們的application-dev.properties
檔案有一組設定:
spring.datasource.url=jdbc:postgresql://${db_server}/mydatabase
spring.datasource.username=${USERNAME}
spring.datasource.password = ${PASSWORD}
redis_host=localhost
spring.redis.host=http://${redis_host}
spring.redis.port=6379
redis_host=localhost
spring.jpa.show-sql=true
upstream.host = myapp.dev.myorg.com
# upstream services
upstream.service.users.url=http://${upstream.host}/api/users
upstream.service.products.url=http://${upstream.host}/api/products
spring.redis.timeout=10000
然後,我們的application-prod.properties
檔案有不同的設定:
spring.datasource.url=jdbc:postgresql://${db_server}/mydatabase
spring.datasource.username=${USERNAME}
spring.datasource.password = ${PASSWORD}
# upstream services
upstream.service.users.url=https://${upstream.host}/api/users
upstream.service.products.url=https://${upstream.host}/api/products
redis_host=azure.redis6a5d54.microsoft.com
spring.redis.host=https://${redis_host}
spring.redis.port=6379
upstream.host = myapp.prod.myorg.com
spring.redis.timeout=2000
在本教程的其餘部分,我們將整理這些文件。
2.2. 屬性檔有什麼問題?
讓我們回顧一下上述文件中的問題:
- 鍵
redis_host
在dev
設定檔中出現了兩次 - 有時,我們的鍵的格式為
name=value
,有時,會有額外的空格 –name = value
- 屬性未按任何邏輯順序排序,檔案的不同部分有多個
spring.redis
鍵 - 兩種配置的「上游服務」 URL 清單基本上相同,但由於
dev
上是http
,而 prod 上是https
所以無法共享。 -
spring.datasource
屬性在檔案之間似乎是相同的,可能是通用application.properties
的候選者
即使這些文件很短,也有很多細節需要考慮,以嘗試改進。
讓我們使用 Spring Properties Cleaner 來實現自動化。然後,讓我們用這個插件來檢查程式碼是否再次出現這種情況。
3. 將 Spring Properties Cleaner 新增到我們的建置中
Spring Properties Cleaner 是一個 Maven 插件。如果它偵測到屬性檔案中的任何問題,就會導致建置失敗。
我們首先將最新版本加入到我們的pom.xml
中:
<plugin>
<groupId>uk.org.webcompere</groupId>
<artifactId>spring-properties-cleaner-plugin</artifactId>
<version>1.0.6</version>
<executions>
<execution>
<goals>
<goal>scan</goal>
</goals>
</execution>
</executions>
</plugin>
現在,如果我們執行 Maven compile
,插件將會報錯:
$ mvn compile
...
[INFO] --- spring-properties-cleaner:1.0.3:scan (default) @ spring-properties-cleaner ---
[INFO] Executing scan on /Users/ashleyfrieze/dev/tutorials/maven-modules/maven-plugins/spring-properties-cleaner/src/main/resources
[ERROR] application-dev.properties: redis_host has duplicate values L5:'localhost',L10:'localhost'
[ERROR] File 'application-dev.properties' does not meet standard - have you run fix?
[ERROR] File 'application-prod.properties' does not meet standard - have you run fix?
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
使用預設配置,插件可以偵測輕微的格式錯誤和重複的按鍵。
4. 修復重複和格式
現在讓我們運行fix
操作來修復這些小錯誤:
$ mvn spring-properties-cleaner:fix
然後,讓我們使用版本控制軟體檢查application-dev.properties
的差異:
逐步修復這些文件,並在進行過程中將更改檢查到我們的版本控制系統中,這是一個非常好的主意。
使用這個預設配置,我們可以看到檔案開頭和下方的一些空格被刪除了。此外,我們也可以看到 duplicate 屬性也被刪除了。
現在插件已經整理好了文件,我們的 Maven 建置將再次成功,除非我們意外地更改這些文件並引入進一步的問題。
但是,我們可以配置插件來控製文件的更多方面。
5. 整理我們的財產
讓我們為插件添加排序配置:
<plugin>
...
<artifactId>spring-properties-cleaner-plugin</artifactId>
...
<configuration>
<sort>clustered</sort>
</configuration>
</plugin>
這裡,我們新增了一個clustered
sort
。我們本來可以選擇sorted
,它會按字母順序排列鍵(將數字視為數字序列)。然而, clustered
會遵循文件中鍵的原始順序,只會移動那些與相同前綴的鍵不同的鍵。
讓我們看看當我們重新運行fix
命令時application-prod.properties
中發生了哪些變化:
這個排序將spring
鍵合併在一起,並將upstream.host
鍵提升到redis_host
之上,並將其留在檔案底部。
在檢查了所有文件的差異之後,我們可以將它們檢查到原始碼控制中並查看其他改進。
6. 內聯前綴
如果我們的目標是重構文件,以便將公共內容放在application.properties
中,那麼我們可能需要處理這樣的情況:兩個文件具有基本相同的值,但不完整的佔位符使得很難找到公共模式。
讓我們再看看範例文件如何表示 URL:
- 在
dev
中:upstream.service.users.url=http://${upstream.host}/api/users
- 在
prod
中:upstream.service.users.url=https://${upstream.host}/api/users
如果upstream.host
佔位符也可以包含該方案,那麼這兩個值將是相同的。
這時就需要用到inlinePrefix
配置了。它接受一個正規表示式,並修改佔位符來內聯重複的前綴:
<configuration>
<inlinePrefix>https?://</inlinePrefix>
</configuration>
此正規表示式可識別https
和http
方案。如果其他鍵也存在此類問題,我們可以進一步自訂它。
讓我們看看它是如何影響我們的applicaton-prod.properties
:
我們可以看到http
方案現在是upstream.host
和redis_host
鍵的一部分。這將使我們能夠將更多通用性提取到application.properties
中。
讓我們檢查這個文件並繼續。
7. 提取到公共屬性
為了設定插件來提取屬性,我們新增了common
配置:
<configuration>
...
<common>full</common>
</configuration>
7.1. 提取full
通用文件
common
提取有三種可能的模式: full
、 consistent
和multiple
。在full
模式下,屬性必須在所有屬性檔案中都相同才能提升到application.properties
檔案。
其他模式, consistent
和multiple
,允許將某些位置出現的屬性新增至通用檔案。這可能會產生副作用,因此需要進行更多檢查。
讓我們看看full
對我們的檔案做了什麼。
它創建了一個新的application.properties
檔案:
spring.datasource.url=jdbc:postgresql://${db_server}/mydatabase
spring.datasource.username=${USERNAME}
spring.datasource.password=${PASSWORD}
spring.redis.host=${redis_host}
spring.redis.port=6379
# upstream services
upstream.service.users.url=${upstream.host}/api/users
upstream.service.products.url=${upstream.host}/api/products
它以通用格式包含了其他文件中的大部分內容。
我們的application-dev.properties
現在小得多:
spring.redis.timeout=10000
spring.jpa.show-sql=true
redis_host=http://localhost
upstream.host=http://myapp.dev.myorg.com
我們的application-prod.properties
也同樣簡化了。
為了讓事情變得更清晰,剩下的唯一方法就是刪除鍵之間的額外換行符號。
7.2. 其他常見文件模式
在我們的範例中,我們使用了full
,這是最安全的選項,因為只有當該屬性在所有特定於設定檔的檔案中都相同時,它才會將屬性提取到通用application.properties
檔案中。
使用consistent
模式,我們可以提取一個屬性,該屬性在所有找到它的檔案中都相同,但並非所有檔案都存在。這會產生副作用,也就是把那些我們可能特別希望從某些設定檔中排除的鍵提升到公用文件中。
在multiple
屬性模式下,如果某個屬性出現在多個屬性檔案中,並且具有最常用的值,則會將其提取到通用檔案中。假設我們在兩個屬性檔中將spring.redis.timeout
設定為10000
,在第三個屬性檔中將其設為20000
在此模式下,值10000
將保存在通用的application.properties
檔案中,而具有異常值的檔案將保留其自訂值。
當我們開始在檔案之間移動不一致的屬性時,可能會出現某些屬性變成通用屬性的風險,以至於我們需要在特定於設定檔的檔案中覆寫它們。這些變更需要更仔細的檢查和測試。
8. 清理垂直空白
我們可以使用remove
刪除所有垂直空格,但讓我們使用section
設定在具有不同前綴的檔案中區塊之間放置空格:
<configuration>
...
<whitespace>section</whitespace>
</configuration>
當我們最後一次執行 fix 指令時,空格就被整理好了:
現在,我們的屬性檔案乾淨整潔。
9. 結論
在本教程中,我們研究了隨著屬性檔案變大,事情可能會變得混亂的一些原因。
我們安裝了 Spring Properties Cleaner Maven 插件,並用它來監控建置過程中的問題。然後,我們增強了它的配置,選擇了一些選項來對檔案進行排序和簡化,最後將屬性提取到一個集中的application.properties
檔案中。
透過使用這個插件作為 linter,我們既可以整理我們的屬性文件,又可以防止問題再次發生。
與往常一樣,範例程式碼可在 GitHub 上取得。