KotlinScript構(gòu)建SpringBootStarter保姆級教程
引言
因業(yè)務(wù)需要, 公司內(nèi)需要使用 SpringBoot Starter 構(gòu)建 SDK. 不同的是使用了更為靈活的 Kotlin 語言, 構(gòu)建腳本也換成了 Kotlin Script.
- 框架: SpringBoot
- 業(yè)務(wù)代碼語言: Kotlin
- 構(gòu)建工具: Gradle
- 構(gòu)建腳本: Kotlin Script (不同于 Groovy, 是 Kotlin 自家的 DSL, 文件后綴為
.kts) - 開發(fā)工具: Idea CE
本文主要分幾個步驟:
- 用 Kotlin 寫一個簡單 SpringBoot Starter
- 進階一: 復(fù)雜配置參數(shù)的寫法
- 進階二: starter 單元測試
- 使用 Kotlin Script 構(gòu)建成 Maven 依賴
- 集成測試
不會太詳細, 但會把主要的內(nèi)容和要注意的點記錄下來.
一 如何用 Kotlin 寫一個簡單 SpringBoot Starter
1 分析
SpringBoot Starter 實現(xiàn)的原理網(wǎng)絡(luò)上已經(jīng)有很多, 就不細說了, 我總結(jié)了一下核心的運作邏輯, 就是下面我畫的這張圖:

所以要寫一個 starter, 無論用什么語言本質(zhì)上都是一樣的.
以下步驟可能與部分網(wǎng)絡(luò)教程不太一樣, 主要是根據(jù)上面的圖方向來分析說明的, 是一個按照邏輯需求來定義的順序:
- 在
resources下新建META-INF文件夾, 寫個spring.factories文件 (文件內(nèi)容見后文), 用于指定一個配置類. - 寫配置類, 主要職能是業(yè)務(wù) Bean 與 其相關(guān)配置的樞紐, 它將對業(yè)務(wù) Bean 進行配置, 配置的內(nèi)容來源于后面我們自己定義的配置文件寫法.
- 寫業(yè)務(wù) Bean, 也就是想讓別人引用這個 starter 依賴后可以使用的類.
- 寫配置屬性聲明類, 是個 POJO 類, 聲明了可以在
application.properties或者application.yml里能使用的配置屬性 - 可選, 寫一個 json 文件用于給使用者寫
application.properties的時候提示一些信息
實際寫代碼時順序按需即可.
2 簡單案例設(shè)計
比如, 我想實現(xiàn)一個郵件告警的 SDK.
這個 SDK 有一個類 AlarmByEmails, 集成此 SDK 的項目通過如下的 application.properties 配置后, 可通過 AlarmByEmails 的某個方法調(diào)用 xxx@163.com 發(fā)送郵件給 yyy@163.com.
(考慮到后續(xù) starter 測試用 yml 方式有所不便, 所以 starter 中測試使用 properties 文件)
simple.alarm.email.host=smtp.163.com # 郵件協(xié)議服務(wù)器 simple.alarm.email.senderEmail=xxx@163.com # 發(fā)送方郵箱 simple.alarm.email.senderPassword=xxx # 發(fā)送方郵箱的授權(quán)碼, 非密碼 simple.alarm.email.receiverEmail=yyy@163.com # 接收方郵箱
怎么實現(xiàn)呢?
3 代碼實現(xiàn)
看個總體目錄結(jié)構(gòu)(已刪減無關(guān)文件):
├── 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 創(chuàng)建的約定俗成規(guī)范 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 被引用后, 程序啟動階段在讀取相應(yīng)配置時, 如果文件配置與默認配置不一樣的話會報錯.
- Spring 在處理這個類的時候會自動屬性注入, 如果不寫缺省值的話啟動找不到注入值會報錯.
業(yè)務(wù) Bean
屬性聲明好了, 該到用的時候了.
class AlarmByEmail(
private val host,
private val senderEmail,
private val senderPassword,
private val receiverEmail
) {
fun sendMessage(content: String): Boolean {
// 發(fā)郵件的實現(xiàn)
}
}
此處使用了構(gòu)造器注入的方式, 也可以使用 setter 方式.
配置類: xxxAutoConfiguration
這是關(guān)鍵, 上面配置上的屬性和業(yè)務(wù) Bean 都有了, 如何把它倆關(guān)聯(lián)起來并注冊成 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 提供的一種注解, 它的作用是按照設(shè)定的條件進行判斷, 把滿足判斷條件的 Bean 注冊到 Spring 容器. 相關(guān)注解如下:
| 條件注解 | 作用 |
|---|---|
| @ConditionalOnBean | 當(dāng)上下文存在某個對象時才會實例化 Bean |
| @ConditionalOnClass | 某個 Class 位于 classpath 路徑上才會實例化 Bean |
| @ConditionalOnExpression | 當(dāng) SpEL 表達式值為 true 的時候才會實例化 Bean |
| @ConditionalOnMissingBean | 當(dāng)上下文不存在某個對象時才會實例化 Bean |
| @ConditionalOnMissingClass | 某個 Class 不在 classpath 路徑上才會實例化 Bean |
| @ConditionalOnNotWebApplication | 非 web 應(yīng)用才會實例化 Bean |
| @ConditionalOnWebApplication | web 應(yīng)用才會實例化 Bean |
| @ConditionalOnProperty | 當(dāng)指定的屬性有指定的值時才會實例化 Bean |
| @ConditionalOnJava | 當(dāng) JVM 版本為指定的版本范圍時才會實例化 Bean |
| @ConditionalOnResource | 當(dāng) classpath 路徑下有指定的資源時才會實例化 Bean |
| @ConditionalOnJndi | 在 JNDI 存在時才會實例化 Bean |
| @ConditionalOnSingleCandidate | 當(dāng)指定的 Bean 在容器中只有一個, 或者有多個但是指定了首選的 Bean 時, 才會實例化 Bean |
@EnableConfigurationProperties , 用于獲取配置聲明類, 原理不贅述.
spring.factories 文件
這個文件是上面寫好的自動配置的入口, 有了它 Spring 才能讀到上面寫好的內(nèi)容:
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": "郵件服務(wù)器地址."
},
{
"name": "simple.alarm.email.senderEmail",
"type": "java.lang.String",
"description": "發(fā)送者郵箱."
},
{
"name": "simple.alarm.email.senderPassword",
"type": "java.lang.String",
"description": "發(fā)送者授權(quán)碼."
},
{
"name": "simple.alarm.email.receiverEmail",
"type": "java.lang.String",
"description": "接收者郵箱."
},
]
}
二 進階: 復(fù)雜配置參數(shù)的寫法
如果我想通過配置配多個發(fā)送者的郵箱, 每個郵箱又可以配置, 該如何實現(xiàn)呢?
比如, 使用 xxx@163.com 發(fā)送郵件給 yyy@163.com, 而使用 yyy@163.com 則可以同時發(fā)郵件給 zzz@163.com 和 xxx@163.com.
配置的寫法:
simple.alarm.email.configs[0].host=smtp.163.com simple.alarm.email.configs[0].senderEmail=xxx@163.com simple.alarm.email.configs[0].senderPassword=xxx simple.alarm.email.configs[0].receivers[0]=yyy@163.com simple.alarm.email.configs[1].host=smtp.163.com simple.alarm.email.configs[1].senderEmail=yyy@163.com simple.alarm.email.configs[1].senderPassword=yyy simple.alarm.email.configs[1].receivers[0]=zzz@163.com simple.alarm.email.configs[1].receivers[0]=xxx@163.com
將郵箱按發(fā)送者分成了一個個的 configs 數(shù)組, 每個 configs 下面保存了發(fā)送的配置, 同時接收者也配置成了數(shù)組,
這樣就完美符合需求了.
那么 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()
)
因為參數(shù)抽出來了, 所以 AlarmByEmail 的入?yún)⒁惨鄳?yīng)調(diào)整:
class AlarmByEmail(
private val configs: Array<EmailConfigEntity>
) {
fun sendMessage(content: String): Boolean {
// 發(fā)郵件的實現(xiàn)
}
}
SimpleAlarmAutoConfiguration 相應(yīng)調(diào)整:
@Configuration
@ConditionalOnClass(SimpleAlarmAutoConfiguration::class)
@EnableConfigurationProperties(value = [EmailProperties::class])
class SimpleAlarmAutoConfiguration {
@Bean
fun alarmByEmail(properties: EmailProperties): AlarmByEmail {
return AlarmByEmail(
properties.configs
)
}
}
這樣就全部完成了.
三 進階: Starter 單元測試
測試是必要的.
單獨的 Spring-boot-starter 并不是一個完整的應(yīng)用 大多數(shù)時候都是作為一個實際應(yīng)用的一部分存在 如果是通過另一個項目引用并啟動項目的話, 會在 Debug 時造成不必要的麻煩 所以需要創(chuàng)建能夠獨立運行的 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].senderEmail=xxx@163.com simple.alarm.email.configs[0].senderPassword=xxx simple.alarm.email.configs[0].receivers[0]=yyy@163.com simple.alarm.email.configs[1].host=smtp.163.com simple.alarm.email.configs[1].senderEmail=yyy@163.com simple.alarm.email.configs[1].senderPassword=yyy simple.alarm.email.configs[1].receivers[0]=zzz@163.com simple.alarm.email.configs[1].receivers[0]=xxx@163.com
測試類
如下, 通過注解指定自動配置類和配置文件
@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 構(gòu)建成 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 目錄下了.
或者在右側(cè) gradle 窗口中也能找到相應(yīng)的 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].senderEmail=xxx@163.com simple.alarm.email.configs[0].senderPassword=xxx simple.alarm.email.configs[0].receivers[0]=yyy@163.com simple.alarm.email.configs[1].host=smtp.163.com simple.alarm.email.configs[1].senderEmail=yyy@163.com simple.alarm.email.configs[1].senderPassword=yyy simple.alarm.email.configs[1].receivers[0]=zzz@163.com simple.alarm.email.configs[1].receivers[0]=xxx@163.com
或者 application.yml
simple:
alarm:
email:
configs:
- host: smtp.163.com
senderEmail: xxx@163.com
senderPassword: xxx
receivers:
- yyy@163.com
- host: smtp.163.com
senderEmail: yyy@163.com
senderPassword: yyy
receivers:
- zzz@163.com
- xxx@163.com
代碼
根據(jù)實際業(yè)務(wù)集成, 具體代碼略.
示例代碼:
以上就是KotlinScript 構(gòu)建 SpringBootStarter保姆級教程的詳細內(nèi)容,更多關(guān)于KotlinScript 構(gòu)建 SpringBootStarter的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)之KMP算法的實現(xiàn)
這篇文章主要為大家詳細介紹了Java數(shù)據(jù)結(jié)構(gòu)中KMP算法的原理與實現(xiàn),文中的示例代碼講解詳細,對我們學(xué)習(xí)Java有一定的幫助,需要的可以參考一下2022-11-11
jeefast和Mybatis實現(xiàn)三級聯(lián)動的示例代碼
這篇文章主要介紹了jeefast和Mybatis實現(xiàn)三級聯(lián)動的示例代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10
Struts2實現(xiàn)生成動態(tài)驗證碼并驗證實例代碼
這篇文章主要介紹了Struts2實現(xiàn)生成動態(tài)驗證碼并驗證實例代碼的相關(guān)資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-06-06
Java Web實現(xiàn)session過期后自動跳轉(zhuǎn)到登陸頁功能【基于過濾器】
這篇文章主要介紹了Java Web實現(xiàn)session過期后自動跳轉(zhuǎn)到登陸頁功能,涉及java過濾器針對session的判斷與跳轉(zhuǎn)相關(guān)操作技巧,需要的朋友可以參考下2017-11-11

