哈喽,好久不见,看了一眼推送记录,我已经差不多一个月没发文章了,忙->累->懒,所以一直没写,最近答辩结束了,也终于可以抽出时间分享点最近学的东西~
关于kotlin
去年还是前年谷歌为kotlin背书的时候我就注意到这个语言了,还给团队买了好几本kotlin的书,总之我是比较看好这个语言的发展的,不过一直没有上手用过,这次就拿kotlin和springboot框架来试试水,期待kotlin的优雅语法搭配spring的强大生态能擦出什么样的火花~
PS:本文适合具备Spring开发与Gradle使用基础的同学食用
创建项目
进入正题了嗷,同学们注意了。
首先我们创建一个gradle脚本保存在空目录中,用来初始化项目:
buildscript {
ext.kotlin_version = '1.3.72' // Required for Kotlin integration
ext.spring_version = '5.2.6.RELEASE'
ext.spring_boot_version = '2.3.0.RELEASE'
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // Required for Kotlin integration
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" // See https://kotlinlang.org/docs/reference/compiler-plugins.html#spring-support
classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version"
}
}
apply plugin: 'kotlin' // Required for Kotlin integration
apply plugin: "kotlin-spring" // https://kotlinlang.org/docs/reference/compiler-plugins.html#spring-support
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
repositories {
jcenter()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.springframework.boot:spring-boot-starter-web"
compile "com.fasterxml.jackson.module:jackson-module-kotlin"
testCompile('org.springframework.boot:spring-boot-starter-test')
}
保存之后gradle build一下,这一步用springboot开发基础的同学都应该知道,这里就不浪费篇幅贴操作方法了。
构建完成之后会生成src文件夹,我们就在src/main下面创建我们的包结构和代码就行,和Java唯一不同的是java文件夹换成了kotlin文件夹。
编写第一个接口
本节代码参考自Kotlin语言官方例子~
首先我们创建一个简单的model,在model包下新建Greeting.kt
,编写一个数据类:
data class Greeting(val id: Long, val content: String)
OK,接下来写控制器的逻辑代码,在controller包下创建GreetingController.kt
,编写代码如下:
@RestController
class GreetingController {
val counter = AtomicLong()
@GetMapping("/greeting")
fun greeting(@RequestParam(value = "name", defaultValue = "World") name: String) =
Greeting(counter.incrementAndGet(), "Hello, $name")
}
看这代码,是不是用springboot熟悉的内味了~
接下来执行gradlew bootRun
任务,就可以启动了,整体流程和使用Java的springboot开发并无二样,只不过有了kotlin语法的加持,比啰嗦的Java写起来更舒服了而已。
在浏览器访问http://127.0.0.1:8080/greeting
,效果如下:
{
"id": 1,
"content": "Hello, World"
}
引入ORM
刚才只是牛刀小试,接下来准备正式开发,通常web系统都需要访问数据库,之前用Java时,很多人会选择jpa或者是mybatis-plus之类的,不过既然我们用了kotlin,就要换上专门为kotlin打造的ORM了,像这种ORM有很多选择,我在某个博客的推荐下,选择了一款国人开发的Ktorm
,顾名思义,kotlin专用
官网文档:https://ktorm.liuwj.me/zh-cn/quick-start.html
对于没用过的同学,可以试试这款ORM框架,除了没有数据库迁移功能之外,勉强还符合我的要求,现在也支持data class作为实体类了,不过我写的时候根据官方的文档是使用interface作为实体类的,这点在控制器传参数的时候小坑。
(PS:有上生产环境需求的同学还是别作死了,这个ORM坑挺多的)
另外有一点,我根据官方在GitHub提供的ktorm和springboot整合的项目,没办法跑起来,不知道啥问题。
进入开发正题了~
首先在gradle脚本里面添加ktorm和spring-jdbc的相关依赖,这里我遇到一个坑,就是官方文档说用spring的jdbc,结果我照做一直都无法读取application配置,无法注入DataSource,然后换成了springboot的jdbc,就可以了,个人项目果然还是有些坑的。
首先定义个变量:
ext.ktorm_version = '2.7.2'
接下来添加依赖,我习惯开发环境用sqlite,需要MySQL的同学请自行替换:
compile "me.liuwj.ktorm:ktorm-core:$ktorm_version"
compile "me.liuwj.ktorm:ktorm-jackson:$ktorm_version"
compile "me.liuwj.ktorm:ktorm-support-sqlite:$ktorm_version"
compile "io.github.willena:sqlite-jdbc:3.30.1"
写配置类:
@Configuration
class KtormConfiguration {
@Autowired
lateinit var dataSource: DataSource
@Bean
fun database(): Database {
return Database.connectWithSpringSupport(
dataSource = dataSource,
dialect = SQLiteDialect()
)
}
@Bean
fun ktormModule(): Module {
return KtormModule()
}
}
编写实体类
这里用到了官方例子的数据结构:
import me.liuwj.ktorm.entity.Entity
interface Department : Entity<Department> {
companion object : Entity.Factory()val id: Intvar name: Stringvar location: String
}
再写一个类来定义表结构,同时与实体类进行列绑定:
import me.liuwj.ktorm.schema.Table
import me.liuwj.ktorm.schema.int
import me.liuwj.ktorm.schema.varchar
object Departments : Table("t_department") {val id by int("id").primaryKey().bindTo { it.id }val name by varchar("name").bindTo { it.name }val location by varchar("location").bindTo { it.location }
}
这里只是用了一个例子,接下来的代码中可能并不会用到这个实体类,不过大家可以举一反三。
开始CRUD
接下来是Web开发程序员的传统艺能,CRUD复制得飞起,开始吧~
看代码了,标准的RESTFul规范接口,分别用到Get、Post、Put、Delete四个HTTP方法,这个组织形式就和DjangoRestFramework一模一样,接口简洁且表达能力丰富。
同时展示了使用ktorm的数据库操作,看出来使用还是挺方便的。
@RestController
@RequestMapping("/quipment/group")
class quimentGroupController {
@Autowired
lateinit var database: Database
@GetMapping("/")
fun list(): Response> =
Response(true, "获取器列表", database.sequenceOf(EquipmentsGroup).toList())@GetMapping("/{id}")fun get(@PathVariable id: Int): Response {val group = database.sequenceOf(EquipmentsGroup).find { it.id eq id } ?: return Response(false, "器材组不存在", null)return Response(true, "获取组信息成功", group)
}@PostMapping("/")fun create(@RequestParam("name") name: String): Response {val group = EquipmentGroup()
group.name = nameval result = database.sequenceOf(EquipmentsGroup).add(group)return if (result > 0) {
Response(true, "创材组成功", group)
} else {
Response(false, "创建组失败", null)
}
}@PutMapping("/{id}")fun update(@PathVariable("id") id: Int,@RequestParam("name") name: String
): Response {val group = database.sequenceOf(EquipmentsGroup).find { it.id eq id } ?: return Response(false, "器材组不存在", null)
group.name = name
group.flushChanges()return Response(true, "修改组信息成功", group)
}@DeleteMapping("/{id}")fun delete(@PathVariable("id") id: Int): Response<Nothing> {val group = database.sequenceOf(EquipmentsGroup).find { it.id eq id }
?: return Response(false, "组不存在", null)
group.delete()return Response(true, "删除组信息成功", null)
}
}
集成Swagger文档
接口写完了,下一步当然是发给前端对接,这时候马上祭出swagger~
集成非常方便,第一步,引入依赖:
compile "io.springfox:springfox-swagger2:2.9.2"
compile "io.springfox:springfox-swagger-ui:2.9.2"
第二步,写配置:
@Configuration
@EnableSwagger2
class SwaggerConfig {
@Bean
fun api(): Docket {
return Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo())
}
private fun apiInfo(): ApiInfo {
return ApiInfo(
"kotlin-springboot",
"博客:http://blog.deali.cn",
"api v1.0",
"http://blog.deali.cn",
Contact(
"DealiAxy",
"http://blog.deali.cn",
"admin@deali.cn"
),
"GPL v2",
"http://www.apache.org/",
Collections.emptyList()
)
}
}
第三步,给接口加上注解,其实这一步也不是必备的,只不过加了之后在接口网页里面可以很清晰的看到每个接口都是要什么功能。
最后可以打开浏览器看看效果了:http://127.0.0.1:8080/swagger-ui.html
,效果大致如下:
这部分的配置可以参考:https://www.ibm.com/developerworks/cn/java/j-using-swagger-in-a-spring-boot-project/index.html
Java代码自行脑补成kotlin的即可~
构建与部署
最后是要把我们写好的项目部署到服务器上,使用以下部署,可执行jar包配合docker,比python方便多了~ (Go和C#表示论部署在座的各位都是辣鸡hhhh)
构建可执行的jar包
执行以下命令:
gradlew bootJar
会在build/libs
目录下生成一个jar包,把根目录的db.sqlite3
数据库文件复制进去同个目录
然后执行:
java -jar xxx.jar
即可启动服务,这个直接上传到服务器就可以运行了。
下一步是使用docker来部署。
docker部署
dockerfile如下:
FROM java:8
MAINTAINER DealiAxy # VOLUME 指定了临时文件目录为/tmp。在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmpVOLUME /tmp# 将jar包添加到容器中并更名为app.jarADD build/libs/spring-boot-restful.jar app.jar# 添加数据库ADD db.sqlite3 db.sqlite3# 运行jar包RUN bash -c 'touch /app.jar'
加上我喜欢的docker-compose部署
version: "3"
services:
web:
restart: always # 除正常工作外,容器会在任何时候重启,比如遭遇 bug、进程崩溃、docker 重启等情况。
build: .
environment:
- ENVIRONMENT=docker
command: java -Djava.security.egd=file:/dev/./urandom -jar /app.jar
ports:
- "8001:9123"
最后总结
Kotlin语言给我的感觉,像是Java、TypeScript、Python的混合,比起C#我觉得还差了点,但是已经甩了Java好几条大街了,配合上Spring无敌的生态,写后台接口,写模板渲染的网站,还是很快很方便的,有了这次的Kotlin学习经验,以后有一些项目需要用到后台我也会考虑一下拿这套技术练练手~
最后感谢Kotlin,然后能避开Java这个屎坑使用Springboot生态开发接口,哈哈哈哈哈溜了