保姆级, 使用 KotlinScript 构建 SpringBootStarter

news/2024/5/4 13:35:07/文章来源:https://blog.csdn.net/Huangjiazhen711/article/details/127048488

因业务需要, 公司内需要使用 SpringBoot Starter 构建 SDK. 不同的是使用了更为灵活的 Kotlin 语言, 构建脚本也换成了 Kotlin Script.

.kts

本文主要分几个步骤:

用 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 的某个方法调用xxx@163.com 发送邮件给yyy@163.com.

(考虑到后续 starter 测试用 yml 方式有所不便, 所以 starter 中测试使用 properties 文件)


simple.alarm.email.host=smtp.163.com # 邮件协议服务器
simple.alarm.email.senderEmail=xxx@163.com	# 发送方邮箱
simple.alarm.email.senderPassword=xxx	# 发送方邮箱的授权码, 非密码
simple.alarm.email.receiverEmail=yyy@163.com	# 接收方邮箱

怎么实现呢?

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 {@Beanfun alarmByEmail(properties: EmailProperties): AlarmByEmail {return AlarmByEmail(properties.host,properties.senderEmail,properties.senderPassword,properties.receiverEmail)}
}

就是如此简单.

@Configuration + @Bean 老组合了, 将一个类注册为 Spring Bean.

@ConditionalOnClass, 是基于 @Conditional 的条件注解, 是 Spring4 提供的一种注解, 它的作用是按照设定的条件进行判断, 把满足判断条件的 Bean 注册到 Spring 容器. 相关注解如下:

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": "接收者邮箱."},]
}

二 进阶: 复杂配置参数的写法
如果我想通过配置配多个发送者的邮箱, 每个邮箱又可以配置, 该如何实现呢?

比如, 使用xxx@163.com 发送邮件给yyy@163.com, 而使用yyy@163.com 则可以同时发邮件给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.comsimple.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

将邮箱按发送者分成了一个个的 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 {@Beanfun 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].senderEmail=xxx@163.com
simple.alarm.email.configs[0].senderPassword=xxx
simple.alarm.email.configs[0].receivers[0]=yyy@163.comsimple.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 {@Testfun contextLoads() {}@Autowiredlateinit var alarmByEmail: AlarmByEmail@Testfun 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].senderEmail=xxx@163.com
simple.alarm.email.configs[0].senderPassword=xxx
simple.alarm.email.configs[0].receivers[0]=yyy@163.comsimple.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.comsenderEmail: xxx@163.comsenderPassword: xxxreceivers:- yyy@163.com- host: smtp.163.comsenderEmail: yyy@163.comsenderPassword: yyyreceivers:- zzz@163.com- xxx@163.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_14356.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【计算机组成原理】中央处理器(四)—— 控制器的功能和工作原理

【计算机组成原理】中央处理器&#xff08;四&#xff09;—— 控制器的功能和工作原理一、前言二、硬布线控制器&#xff08;一&#xff09;、安排微操作时序的原则1. 安排微操作时序-取指周期2. 安排微操作时序-间址周期3. 安排微操作时序-执行周期4. 安排微操作时序-间址周期…

Python算法性能分析-时间复杂度

时间复杂度&#xff1a; 算法的运行时间。 什么是大O&#xff1a; 大O用来表示上界的。 数据规模&#xff1a; 在决定使用哪些算法的时候&#xff0c;不是时间复杂越低的越好&#xff08;因为简化后的时间复杂度忽略了常数项等等&#xff09;&#xff0c;要考虑数据规模&am…

没有项目经验,如何书写漂亮的简历?

嗨&#xff0c;同学 你们是不是也开始 国庆假期倒计时啦&#xff01;&#xff01;&#xff01; 一想到熬过这周&#xff0c;接下来可以嗨7天7夜 就按捺不住自己内心的雀跃&#xff01; 但是,有人却高兴不起来&#xff0c;因为在这个“金九银十”&#xff0c;一些同学还没找到…

接口(关注我还有后续哦)

&#x1f44d; 棒棒有言&#xff1a;现在学习Java变得比以前容易多了&#xff0c;除了有大量的视频教程外&#xff0c;还有专业的机构&#xff0c;这都使学习变得更加简单化。如果仅仅学了些皮毛&#xff0c;高手写的程序你是望尘莫及的。在学习的过程中&#xff0c;书籍永远是…

后台系统接入udesk在线客服(vue前端方式)

SDK最舒服的一点就是买来服务&#xff0c;直接Ctrl CV脚本进项目基本就能完成目标功能&#xff0c;要做的无非就是自定义属性的添加。 楼上项目组用的是java后端接入&#xff0c;我这儿是vue前端接入&#xff0c;做法略有不同。 简单点做就是复制上面script标签内代码到index.h…

关于SignalR的内容延续:1.协商协议 2.分布式部署

既然项目中用到了&#xff0c;那就搞搞清楚&#xff0c;搞不懂就死 : > 前置内容&#xff1a; 长轮询问题在ABP中的解决方案&#xff0c;SignalR_董厂长的博客-CSDN博客 “SingalR是对webSocekt的封装” &#xff0c;这句话是片面的。 因为&#xff1a; SignalR支持多…

vue-----组件通信/传值

一 父子组件通信分为父给子传和子给父传 父给子传&#xff1a; 1.在子组件标签中写传入的值 2.在子组件内使用props接收父组件传递的值。 子给父传&#xff1a; 1.在子组件内部使用$emit发射自定义事件和传递给父组件的值 2.在父组件内声明自定义事件接受参数 二 兄弟组件…

真无线蓝牙耳机哪款音质最好?真无线蓝牙耳机音质排行榜

随着蓝牙技术的飞速发展&#xff0c;很多耳机的质量和质量都很好。喜欢音乐的人&#xff0c;往往会沉迷于这种美妙的感觉&#xff0c;也正是因为如此&#xff0c;他们才会对音质有更高的要求。除了音质之外&#xff0c;还有很多新的特性&#xff0c;例如主动降低噪音、声音操控…

全流程调度

目录 Azkaban 配置mysql 配置 Executor Server 配置Web Server Sqoop导出脚本 Azkaban 安装azkaban并改名 配置mysql 启动 [doudouhadoop102 ~]$ mysql -uroot -p123456登陆 MySQL&#xff0c;创建 Azkaban 数据库 mysql> create database azkaban;设置密码有效长度 …

一文入门Qt Quick

很高兴可以来到这一章,终于可以开始讲讲最近几年Qt的热门技术Quick这一块了。希望通过这个比较简短的例子可以带领有兴趣的朋友快速跨过Qt Quick的入门这道槛!以下内容为本人的著作,如需要转载,请声明原文链接 微信公众号「englyf」https://www.cnblogs.com/englyf/p/16733…

m基于matlab的光通信的信道估计,均衡,抑制papr误码率仿真,对比ZF,RLS,MMSE三种算法(包括matlab仿真录像)

目录 1.源码获取方式 2.算法描述 3.部分程序 4.部分仿真图预览 1.源码获取方式 使用版本matlab2013b 获取方式1&#xff1a; 点击下载链接&#xff08;解压密码C123456&#xff09;&#xff1a; m基于matlab的光通信的信道估计&#xff0c;均衡&#xff0c;抑制papr误码…

libxml编译时问题解决记录

在对libxml进行模糊测试时&#xff0c;需要先将其拉去并进行编译&#xff0c;可参考此链接&#xff1a;magma本地编译 或者直接参考这个链接&#xff1a;magma编译libxml2 然而在编译的过程中&#xff0c;拉去完libxml2执行到这一句时报错如下&#xff1a; configure.ac:42: e…

Python骚操作,实现驾考自动答题,这就直接满分了?

Python骚操作来了~ 用Python来实现科目一/四自动答题&#xff0c;100分不要太简单&#xff01; 最初是表弟最近想买车&#xff0c;但是驾照都没有&#xff0c;买什么车&#xff0c;只能先考驾照~ 看他在网页上练习题目慢吞吞的&#xff0c;我就看不下去了&#xff0c;直接给他…

《数据结构》队列及其经典面试题

前言 上一篇讲了栈和栈的经典面试题&#xff0c;链接如下&#xff1a; 栈与栈的经典面试题 其实栈和队列是一码事&#xff0c;都是对只能再线性表的一端进行插入和删除。 因此&#xff0c;其实栈和队列可以互相转换&#xff01; 一、队列的特点 先进先出的数据结构&#…

Android系统安全 — 2.0-移动终端栈溢出的保护机制设置

简介 操作系统提供了许多安全机制来尝试降低或阻止缓冲区溢出攻击带来的安全风险。例如 NX/DEP、 ASLR&#xff08;PIE&#xff09;、CANARY、FORTIFY、RELRO 等手段。 栈保护 1.NX/DEP Linux 和 Windows 平台都支持对非可执行代码的保护&#xff0c;在 Linux 平台中被称为…

【Mybatis框架】初识Mybatis

CSDN话题挑战赛第2期 参赛话题&#xff1a;学习笔记 MyBatis1、MyBatis简介1.1、MyBatis历史1.2、MyBatis特性2. 搭建MyBatis2.1 创建一个Maven项目2.2 在项目下新建我们的MyBatis项目2.3 引入依赖2.4 创建MyBatis的核心配置文件2.5 创建mapper接口2.6 创建MyBatis的映射文件2.…

AWS 中文入门开发教学 34- MySQL@RDS - 准备工作 - VPC子网,安全组,DB子网组,参数组,选项组

知识点 建立RDS MySQL前的准备工作实战演习 VPC子网,安全组,DB子网组,参数组,选项组 VPC子网 Name: deeplearnaws-db-1cCIDR: 172.16.21.0/24 安全组 Name: deeplearnaws-db-sg <- 可以直接使用之前创建的,但生产环境时应只保留3306端口 DB子网组 Name: deeplearnaws-db-su…

JavaScript学习Day008(jQuery操作)

DOM操作分类 DOM操作分为三类 DOM Core&#xff1a;任何一种支持DOM的编程语言都可以使用它&#xff0c;如getElementById() HTML-DOM&#xff1a;用于处理HTML文档&#xff0c;如document.forms CSS-DOM&#xff1a;用于操作CSS&#xff0c;如element.style.color"gree…

【NLP】第12章 检测客户情绪以做出预测

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

JavaScript数组与对象

数组对象 「创建数组的两种方式」 1. 字面量方式var arr [1,"test",true];2. 实例化数组对象 new Array() var arr new Array(); 注意&#xff1a;上面代码中arr创建出的是一个空数组&#xff0c;如果需要使用构造函数Array创建非空数组&#xff0c;可以在创建数…