1. 概述
在本教程中,我們將學習如何使用 Pkl(發音為“Pickle”,即醃製),一種配置為代碼(Configuration-as-Code)語言,用於定義 Spring Boot 應用程序中的配置。
傳統上,我們可能使用 YAML、JSON 或基於鍵值對的屬性文件來定義應用程序配置。然而,這些格式是靜態的,驗證屬性具有挑戰性。 此外,隨着配置屬性數量的增加,定義更復雜的層次結構配置變得越來越困難。
因此,通過使用配置為代碼(CaC),如 Pkl、HCL(Hashicorp 配置語言) 和 Dhal 配置語言,可以幫助我們克服靜態屬性文件帶來的挑戰。
2. Pkl 簡介
配置即代碼 (Configuration-as-Code) 是一種流行的概念,它提倡“避免重複” (Don’t Repeat Yourself, DRY) 原則,並有助於簡化配置管理。它採用聲明式編碼風格,提供了一種結構化和高效的方法來定義配置模板。它還提高了配置的可讀性。
Pkl 能夠定義各種元素,例如對象類型、集合、映射和廣泛的基本數據類型。 這種靈活性使其可擴展,並有助於以清晰簡潔的方式輕鬆地對複雜配置進行建模。 此外,其類型和驗證機制有助於在應用程序部署之前捕獲配置錯誤。
此外,Pkl 提供強大的工具支持,以促進易於採用。 它具有用於生成 Java、Kotlin、Go 和 Swift 等不同語言的代碼的工具。 這對於在基於這些編程語言構建的應用程序中嵌入和讀取 Pickle 配置至關重要。
此外,像 IntelliJ 和 VS Code 這樣的 IDE 具有插件,以促進使用 Pkl 進行配置開發。 Pkl 還附帶一個命令行工具 (CLI) 稱為 pkl,可用於評估 Pickle 模塊。 其一項功能是將 .pkl 配置轉換為 JSON、YAML 和 XML 格式。
3. Spring 配置 Java Bean 綁定
定義 Spring 配置最常見的方法是創建屬性文件並使用 @Value 標註注入它們。然而,這種方法通常會導致過多的冗餘代碼。
幸運的是,Spring Framework 簡化了此過程,藉助 @ConfigurationProperties 標註,使配置能夠無縫地綁定到 Java Bean 上。
但是,採用這種方法,我們仍然必須手動創建 Java Bean 並對成員字段進行必要的驗證。因此,配置漂移難以檢測,並且經常會導致應用程序中的關鍵錯誤。因此,像 Pkl 這樣的配置定義語言,以及自動化的 Java 代碼生成支持,是一種更健壯的方式來集成應用程序的配置。
4. Spring Boot 集成
Pkl 提供庫來從 Pickle 文件生成 POJO。稍後在運行時,pkl-spring 庫可以將 Pickle 配置填充到 POJO 中。下面我們將學習如何將其集成到 Spring Boot 應用程序中。
4.1. prerequisites
首先,我們將包含用於 pkl-spring 庫的 Maven 依賴項:
<dependency>
<groupId>org.pkl-lang</groupId>
<artifactId>pkl-spring</artifactId>
<version>0.16.0</version>
</dependency>此外,我們還將使用 exec-maven-plugin 從 Pickle 配置文件的生成 POJO:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>gen-pkl-java-bind</id>
<phase>generate-sources</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>org.pkl.codegen.java.Main</mainClass>
<includeProjectDependencies>false</includeProjectDependencies>
<additionalClasspathElements>
<classpathElement>${pkl-tools-filepath}/pkl-tools-0.27.2.jar</classpathElement>
</additionalClasspathElements>
<arguments>
<argument>${project.basedir}/src/main/resources/ToolIntegrationTemplate.pkl</argument>
<argument>-o</argument>
<argument>${project.build.directory}/generated-sources</argument>
<argument>--generate-spring-boot</argument>
<argument>--generate-getters</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>Maven 插件執行 Java 代碼生成工具 pkl-tools:
java -cp pkl-tools-0.27.2.jar org.pkl.codegen.java.Main \
src/main/resources/ToolIntegration.pkl \
-o ${project.basedir}/src/main --generate-spring-boot假設存在一個名為 ToolIntegrationTemplate.pkl 的 Pickle 文件,工具會在 target/generated-sources 文件夾中生成與 Pickle 文件對應的源代碼。
–generate-spring-boot 參數會將必要的 Spring Boot 類級別的 @ConfigurationProperties 註解包含到 Java Bean 中。 此外,–generate-getters 參數聲明屬性鍵為 private。
對於 Gradle 用户來説,pkl-gladle 插件 提供了一鍵生成 POJO 的支持。
4.2. 創建配置(pkl 格式)
讓我們考慮一個數據集成應用程序,它從源系統(如 Git 和 Jira)中獲取數據,並將它們分發到其他目標系統。
讓我們首先定義 Pickle 模板 ToolIntegrationTemplate.pkl 以存儲連接屬性:
module com.baeldung.spring.pkl.ToolIntegrationProperties
class Connection {
url:String
name:String
credential:Credential
}
class Credential {
user:String
password:String
}
gitConnection: Connection
jiraConnection: Connection總而言之,我們定義了兩個 Pkl 類 Connection 和 Credential,並聲明瞭兩個對象 gitConnection 和 jiraConnection,它們都是 Connection 類型的對象。
接下來,讓我們在 application.pkl 文件中定義 gitConnection 和 jiraConnection:
amends "ToolIntegrationTemplate.pkl"
gitConnection {
url = "https://api.github.com"
name = "GitHub"
credential {
user = "gituser"
password = read("env:GITHUB_PASSWORD")
}
}
jiraConnection {
url = "https://jira.atlassian.com"
name = "Jira"
credential {
user = "jirauser"
password = read("env:JIRA_PASSWORD")
}
}我們已將模板填充了數據源的連接屬性。值得注意的是,我們沒有在配置文件中硬編碼密碼。相反,我們使用 Pkl 的 read() 函數從環境變量中檢索密碼,這是一種更安全的方法。
4.3. 將 Pkl 模板轉換為 POJO
在 Pickle 文件中定義配置後,可以通過執行 exec:java Maven 目標(在 pom.xml 文件中定義)來生成 POJO:
mvn exec:java@gen-pkl-java-bind最終,pkl-tools庫會生成一個POJO和一個屬性文件:
ToolIntegrationProperties類包含兩個內部類,Connection和Credential。它還使用了@ConfigurationProperties註解,這有助於將application.pkl中的屬性綁定到ToolIntegrationProperties對象。
4.4. 將 Pickle 配置綁定到 POJO
讓我們首先定義與源系統集成的服務類 JiraService 和 GitHubService:
public class JiraService {
private final ToolIntegrationProperties.Connection jiraConnection;
public JiraService(ToolIntegrationProperties.Connection connection) {
this.jiraConnection = connection;
}
// ...methods getting data from Jira
}public class GitHubService {
private final ToolIntegrationProperties.Connection gitConnection;
public GitHubService(ToolIntegrationProperties.Connection connection) {
this.gitConnection = connection;
}
// ...methods getting data from GitHub
}兩個服務都具有接受 ToolIntegrationProperties 作為參數的構造函數。隨後,參數中的連接詳情會被分配給類型為 ToolIntegrationProperties.Connection 的類級屬性。
接下來,我們將定義一個配置類,該類實例化服務並將其聲明為 Spring 框架中的 Bean。
@Configuration
public class ToolConfiguration {
@Bean
public GitHubService getGitHubService(ToolIntegrationProperties toolIntegration) {
return new GitHubService(toolIntegration.getGitConnection());
}
@Bean
public JiraService getJiraService(ToolIntegrationProperties toolIntegration) {
return new JiraService(toolIntegration.getJiraConnection());
}
}在應用程序啓動期間,Spring 框架會將方法參數與來自 application.pkl 文件的配置綁定。最終,通過調用服務構造函數來實例化 Bean。
4.5. 服務注入與執行
最後,我們可以使用 <em @Autowired> 註解在其他 Spring 組件中注入服務 Bean。
使用 <em @SpringBootTest>,我們將驗證應用程序是否能夠從 Pickle 文件中加載 Jira 連接配置:
@SpringBootTest
public class SpringPklUnitTest {
@Autowired
private JiraService jiraService;
@Test
void whenJiraConfigsDefined_thenLoadFromApplicationPklFile() {
ToolIntegrationProperties.Connection jiraConnection = jiraService.getJiraConnection();
ToolIntegrationProperties.Credential jiraCredential = jiraConnection.getCredential();
assertAll(
() -> assertEquals("Jira", jiraConnection.getName()),
() -> assertEquals("https://jira.atlassian.com", jiraConnection.getUrl()),
() -> assertEquals("jirauser", jiraCredential.getUser()),
() -> assertEquals("jirapassword", jiraCredential.getPassword()),
() -> assertEquals("Reading issues from Jira URL https://jira.atlassian.com",
jiraService.readIssues())
);
}
}為了演示,我們通過 pom.xml文件將Git和JIRA密碼設置在環境變量中。運行測試後,我們確認JIRA連接和憑據詳情與application.pkl文件中定義的的值相匹配。 此外,我們還假設如果正確實施,JiraService#readIssues()方法將成功執行。 目前,該方法僅返回一個佔位字符串。
同樣,讓我們檢查啓動應用程序後,是否從Pickle文件加載GitHub連接配置:
@SpringBootTest
public class SpringPklUnitTest {
@Autowired
private GitHubService gitHubService;
@Test
void whenGitHubConfigsDefined_thenLoadFromApplicationPklFile() {
ToolIntegrationProperties.Connection gitHubConnection = gitHubService.getGitConnection();
ToolIntegrationProperties.Credential gitHubCredential = gitHubConnection.getCredential();
assertAll(
() -> assertEquals("GitHub", gitHubConnection.getName()),
() -> assertEquals("https://api.github.com", gitHubConnection.getUrl())
() -> assertEquals("gituser", gitHubCredential.getUser()),
() -> assertEquals("gitpassword", gitHubCredential.getPassword()),
() -> assertEquals("Reading issues from GitHub URL https://api.github.com",
gitHubService.readIssues())
);
}
}正如預期的那樣,本次連接和 GitHub 憑據信息也與 application.pkl 文件中定義的相同。因此,憑藉正確的連接信息,我們可以推斷,如果 GitHubServiceService#readIssues() 正確實現,它將連接到 GitHub 並檢索問題。 類似於 JiraService#readIssues(),該方法目前僅返回一個佔位符字符串。
5. 結論
在本文中,我們學習瞭如何利用 Pickle 文件定義 Spring Boot 應用程序的配置及其優勢。然而,掌握 Pkl 語言的概念對於設計可擴展且易於維護的複雜配置同樣至關重要。 此外,瞭解 Spring 框架將外部屬性綁定到 POJO 的功能也至關重要。