KotlinScript構建SpringBootStarter保姆級教程
引言
因業務需要, 公司內需要使用 SpringBoot Starter 構建 SDK. 不同的是使用瞭更為靈活的 Kotlin 語言, 構建腳本也換成瞭 Kotlin Script.
- 框架: SpringBoot
- 業務代碼語言: Kotlin
- 構建工具: Gradle
- 構建腳本: Kotlin Script (不同於 Groovy, 是 Kotlin 自傢的 DSL, 文件後綴為
.kts
) - 開發工具: Idea CE
本文主要分幾個步驟:
- 用 Kotlin 寫一個簡單 SpringBoot Starter
- 進階一: 復雜配置參數的寫法
- 進階二: starter 單元測試
- 使用 Kotlin Script 構建成 Maven 依賴
- 集成測試
不會太詳細, 但會把主要的內容和要註意的點記錄下來.
一 如何用 Kotlin 寫一個簡單 SpringBoot Starter
1 分析
SpringBoot Starter 實現的原理網絡上已經有很多, 就不細說瞭, 我總結瞭一下核心的運作邏輯, 就是下面我畫的這張圖:
所以要寫一個 starter, 無論用什麼語言本質上都是一樣的.
以下步驟可能與部分網絡教程不太一樣, 主要是根據上面的圖方向來分析說明的, 是一個按照邏輯需求來定義的順序:
- 在
resources
下新建META-INF
文件夾, 寫個spring.factories
文件 (文件內容見後文), 用於指定一個配置類. - 寫配置類, 主要職能是業務 Bean 與 其相關配置的樞紐, 它將對業務 Bean 進行配置, 配置的內容來源於後面我們自己定義的配置文件寫法.
- 寫業務 Bean, 也就是想讓別人引用這個 starter 依賴後可以使用的類.
- 寫配置屬性聲明類, 是個 POJO 類, 聲明瞭可以在
application.properties
或者application.yml
裡能使用的配置屬性 - 可選, 寫一個 json 文件用於給使用者寫
application.properties
的時候提示一些信息
實際寫代碼時順序按需即可.
2 簡單案例設計
比如, 我想實現一個郵件告警的 SDK.
這個 SDK 有一個類 AlarmByEmails
, 集成此 SDK 的項目通過如下的 application.properties 配置後, 可通過 AlarmByEmails
的某個方法調用 [email protected] 發送郵件給 [email protected].
(考慮到後續 starter 測試用 yml 方式有所不便, 所以 starter 中測試使用 properties 文件)
simple.alarm.email.host=smtp.163.com # 郵件協議服務器 [email protected] # 發送方郵箱 simple.alarm.email.senderPassword=xxx # 發送方郵箱的授權碼, 非密碼 [email protected] # 接收方郵箱
怎麼實現呢?
3 代碼實現
看個總體目錄結構(已刪減無關文件):
├── build.gradle.kts ├── settings.gradle.kts └── src └── main ├── kotlin │ └── com │ └── looko │ └── simplealarmspringbootstarter │ ├── autoconfigure │ │ ├── SimpleAlarmAutoConfiguration.kt │ │ └── properties │ │ └── EmailProperties.kt │ └── component │ └── AlarmByEmails.kt └── resources ├── META-INF │ └── spring.factories └── test.properties
依賴項
基於 Kotlin 和 Gradle 新建 Spring Boot 項目, 名稱最好按照 Starter 創建的約定俗成規范 xxx-spring-boot-starter
, 刪除啟動類, 然後在 build.gradle.kts
的依賴中添加:
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
配置屬性聲明類: xxxProperties
這裡的屬性就定義瞭配置文件的寫法.
@ConfigurationProperties(prefix = "simple.alarm.email") data class EmailProperties( var host? = null, var senderEmail? = null, var senderPassword? = null, var receiverEmail? = null )
註意:
- 配置文件到 POJO 的屬性裝配是要用到 setter 的, 所以要定義為 var, 如果定義為 val , starter 被引用後, 程序啟動階段在讀取相應配置時, 如果文件配置與默認配置不一樣的話會報錯.
- Spring 在處理這個類的時候會自動屬性註入, 如果不寫缺省值的話啟動找不到註入值會報錯.
業務 Bean
屬性聲明好瞭, 該到用的時候瞭.
class AlarmByEmail( private val host, private val senderEmail, private val senderPassword, private val receiverEmail ) { fun sendMessage(content: String): Boolean { // 發郵件的實現 } }
此處使用瞭構造器註入的方式, 也可以使用 setter 方式.
配置類: xxxAutoConfiguration
這是關鍵, 上面配置上的屬性和業務 Bean 都有瞭, 如何把它倆關聯起來並註冊成 Spring Bean 呢?
@Configuration @ConditionalOnClass(SimpleAlarmAutoConfiguration::class) @EnableConfigurationProperties(value = [EmailProperties::class]) class SimpleAlarmAutoConfiguration { @Bean fun alarmByEmail(properties: EmailProperties): AlarmByEmail { return AlarmByEmail( properties.host, properties.senderEmail, properties.senderPassword, properties.receiverEmail ) } }
就是如此簡單.
@Configuration + @Bean 老組合瞭, 將一個類註冊為 Spring Bean.
@ConditionalOnClass, 是基於 @Conditional 的條件註解, 是 Spring4 提供的一種註解, 它的作用是按照設定的條件進行判斷, 把滿足判斷條件的 Bean 註冊到 Spring 容器. 相關註解如下:
條件註解 | 作用 |
---|---|
@ConditionalOnBean | 當上下文存在某個對象時才會實例化 Bean |
@ConditionalOnClass | 某個 Class 位於 classpath 路徑上才會實例化 Bean |
@ConditionalOnExpression | 當 SpEL 表達式值為 true 的時候才會實例化 Bean |
@ConditionalOnMissingBean | 當上下文不存在某個對象時才會實例化 Bean |
@ConditionalOnMissingClass | 某個 Class 不在 classpath 路徑上才會實例化 Bean |
@ConditionalOnNotWebApplication | 非 web 應用才會實例化 Bean |
@ConditionalOnWebApplication | web 應用才會實例化 Bean |
@ConditionalOnProperty | 當指定的屬性有指定的值時才會實例化 Bean |
@ConditionalOnJava | 當 JVM 版本為指定的版本范圍時才會實例化 Bean |
@ConditionalOnResource | 當 classpath 路徑下有指定的資源時才會實例化 Bean |
@ConditionalOnJndi | 在 JNDI 存在時才會實例化 Bean |
@ConditionalOnSingleCandidate | 當指定的 Bean 在容器中隻有一個, 或者有多個但是指定瞭首選的 Bean 時, 才會實例化 Bean |
@EnableConfigurationProperties , 用於獲取配置聲明類, 原理不贅述.
spring.factories 文件
這個文件是上面寫好的自動配置的入口, 有瞭它 Spring 才能讀到上面寫好的內容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.looko.simplealarmspringbootstarter.autoconfigure.SimpleAlarmAutoConfiguration
json 配置註釋文件
可不寫, 用來作為寫屬性時的提示.
spring-configuration-metadata.json
:
{ "properties": [ { "name": "simple.alarm.email.host", "type": "java.lang.String", "description": "郵件服務器地址." }, { "name": "simple.alarm.email.senderEmail", "type": "java.lang.String", "description": "發送者郵箱." }, { "name": "simple.alarm.email.senderPassword", "type": "java.lang.String", "description": "發送者授權碼." }, { "name": "simple.alarm.email.receiverEmail", "type": "java.lang.String", "description": "接收者郵箱." }, ] }
二 進階: 復雜配置參數的寫法
如果我想通過配置配多個發送者的郵箱, 每個郵箱又可以配置, 該如何實現呢?
比如, 使用 [email protected] 發送郵件給 [email protected], 而使用 [email protected] 則可以同時發郵件給 [email protected] 和 [email protected].
配置的寫法:
simple.alarm.email.configs[0].host=smtp.163.com simple.alarm.email.configs[0][email protected] simple.alarm.email.configs[0].senderPassword=xxx simple.alarm.email.configs[0].receivers[0][email protected] simple.alarm.email.configs[1].host=smtp.163.com simple.alarm.email.configs[1][email protected] simple.alarm.email.configs[1].senderPassword=yyy simple.alarm.email.configs[1].receivers[0][email protected] simple.alarm.email.configs[1].receivers[0][email protected]
將郵箱按發送者分成瞭一個個的 configs 數組, 每個 configs 下面保存瞭發送的配置, 同時接收者也配置成瞭數組,
這樣就完美符合需求瞭.
那麼 properties 等類怎麼寫呢?
EmailProperties
:
@ConfigurationProperties(prefix = "simple.alarm.email") data class EmailProperties( var configs: Array<EmailConfigEntity> = arrayOf() )
這是抽出來的 EmailConfigEntity
, 註意用 var:
data class EmailConfigEntity( var host: String? = null, var senderEmail: String? = null, var senderPassword: String? = null, var receivers: Array<String> = arrayOf() )
因為參數抽出來瞭, 所以 AlarmByEmail
的入參也要相應調整:
class AlarmByEmail( private val configs: Array<EmailConfigEntity> ) { fun sendMessage(content: String): Boolean { // 發郵件的實現 } }
SimpleAlarmAutoConfiguration
相應調整:
@Configuration @ConditionalOnClass(SimpleAlarmAutoConfiguration::class) @EnableConfigurationProperties(value = [EmailProperties::class]) class SimpleAlarmAutoConfiguration { @Bean fun alarmByEmail(properties: EmailProperties): AlarmByEmail { return AlarmByEmail( properties.configs ) } }
這樣就全部完成瞭.
三 進階: Starter 單元測試
測試是必要的.
單獨的 Spring-boot-starter
並不是一個完整的應用 大多數時候都是作為一個實際應用的一部分存在 如果是通過另一個項目引用並啟動項目的話, 會在 Debug 時造成不必要的麻煩 所以需要創建能夠獨立運行的 Test
依賴
testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.boot:spring-boot-test-autoconfigure")
配置文件
resourses
路徑下的 test.properties
:
simple.alarm.email.configs[0].host=smtp.163.com simple.alarm.email.configs[0][email protected] simple.alarm.email.configs[0].senderPassword=xxx simple.alarm.email.configs[0].receivers[0][email protected] simple.alarm.email.configs[1].host=smtp.163.com simple.alarm.email.configs[1][email protected] simple.alarm.email.configs[1].senderPassword=yyy simple.alarm.email.configs[1].receivers[0][email protected] simple.alarm.email.configs[1].receivers[0][email protected]
測試類
如下, 通過註解指定自動配置類和配置文件
@SpringBootTest(classes = [SimpleAlarmAutoConfiguration::class]) @TestPropertySource("classpath:test.properties") class SimpleAlarmSpringBootStarterApplicationTests { @Test fun contextLoads() { } @Autowired lateinit var alarmByEmail: AlarmByEmail @Test fun testAlarmByEmail() { assert(alarmByEmail.sendMessage("Message Content")) } }
四 如何使用 Kotlin Script 構建成 Maven 依賴
使用 maven-publish
插件.
在 build.gradle.kts
中, 主要用法如下 :
// ... plugins { // ... `maven-publish` } // ... val sourcesJar by tasks.registering(Jar::class) { archiveClassifier.set("sources") from(sourceSets.main.get().allSource) } publishing { publications { register("alarm", MavenPublication::class) { groupId = "com.looko" artifactId = "simple-alarm-spring-boot-starter" version = "0.0.1-SNAPSHOT" from(components["java"]) artifact(sourcesJar.get()) } } repositories { maven { mavenLocal() } } } // ...
在 IDEA 界面 double-ctrl 呼出 run 窗口, 找到 gradle publishToMavenLocal
回車就能打包到 .m2
目錄下瞭.
或者在右側 gradle 窗口中也能找到相應的 gradle task.
如果打到倉庫的包裡含有 plain
後綴, 不被 maven 識別的話, 可以在 build.gradle.kts
中添加如下配置解決:
tasks.getByName<Jar>("jar") { archiveClassifier.set("") }
五 集成測試
依賴
testImplementation("com.looko:simple-alarm-spring-boot-starter:0.0.1-SNAPSHOT")
配置文件
application.properties
simple.alarm.email.configs[0].host=smtp.163.com simple.alarm.email.configs[0][email protected] simple.alarm.email.configs[0].senderPassword=xxx simple.alarm.email.configs[0].receivers[0][email protected] simple.alarm.email.configs[1].host=smtp.163.com simple.alarm.email.configs[1][email protected] simple.alarm.email.configs[1].senderPassword=yyy simple.alarm.email.configs[1].receivers[0][email protected] simple.alarm.email.configs[1].receivers[0][email protected]
或者 application.yml
simple: alarm: email: configs: - host: smtp.163.com senderEmail: [email protected] senderPassword: xxx receivers: - [email protected] - host: smtp.163.com senderEmail: [email protected] senderPassword: yyy receivers: - [email protected] - [email protected]
代碼
根據實際業務集成, 具體代碼略.
示例代碼:
gayhub 倉庫
以上就是KotlinScript 構建 SpringBootStarter保姆級教程的詳細內容,更多關於KotlinScript 構建 SpringBootStarter的資料請關註WalkonNet其它相關文章!