资讯专栏INFORMATION COLUMN

Kotlin + Spring Boot服务端开发

e10101 / 3063人阅读

摘要:是什么著名厂商开发的基于的静态类型编程语言,声称。语法近似和,且已活跃在开发领域,被誉为平台的。各有千秋,我更认同改写字节码。的作用是防止敏感字段被泄露到中,的作用是软删除数据不可见,但没有真的删除。

Kotlin是什么?

著名IDE厂商JetBrains开发的基于JVM的静态类型编程语言,声称100% interoperable with Java。Kotlin是由工程师设计的,各种细节设计非常切合工程师的需要。语法近似Java和Scala,且已活跃在Android开发领域,被誉为Android平台的Swift。

Kotlin能与Java混合使用,并且直接复用Java的生态系统(库、框架、工具)。一个已有的Java项目,只需引用Kotlin的Maven/Gradle插件,以及引用Kotlin标准库的依赖,就可以逐渐掺入Kotlin代码。你完全可以当它是a better Java。

Kotlin的学习曲线极其平缓,学习量相当于一个框架。有经验的程序员阅读了文档就能立刻用起来了。不信你看:

原版文档 http://kotlinlang.org/docs/re...

中文文档 http://kotlindoc.com

举几个例子来说明Kotlin的优点吧,上代码:

</>复制代码

  1. //句尾不用写分号
  2. // 自动推导变量类型,无需声明
  3. val a = "Hello"
  4. // 简单的println
  5. println(a.length() == 5)
  6. // 不用写new, 直接调构造函数
  7. val b = String("Hello")
  8. // 字符串插值
  9. "$a $b" == "Hello Hello"
  10. // if-else是表达式, 真方便!
  11. // ==相当于equals, 再也不怕忘写equals了!
  12. val oneOrTwo = if (a == "Hello") 1 else 2
  13. // ===相当于Java的==
  14. (a === b) == false
  15. // Lambda用{}包起来,若有唯一参数,参数名默认为it
  16. // 集合的函数式操作, 无需Java 8繁琐的stream.collect(Collectors.toList())
  17. listOf(-1, 0, 1).map{it + 1}.filter{it > 0} == listOf(1, 2)
  18. // ?. (null-safe调用)
  19. // ?: (用默认值给null兜底)
  20. val numStr = getNumberOrNull()?.toString() ?: ""
  21. // 自动关闭的资源
  22. FileInputStream("MyFile").use { stream -> // 可指定参数名为stream, 取代默认的it
  23. val firstByte = stream.read()
  24. }
  25. // 可以更简单,一行
  26. val fileContent = File("MyFile").readText()
  27. // lazy, 延迟初始化
  28. class CPU {
  29. val cpuCores by lazy { Runtime.getRuntime().availableProcessors() }
  30. }

Kotlin为厌烦Java而疑虑Scala的人提供了避风港,为喜欢Groovy而想要静态类型的人提供了避风港。啊!生活。

Spring Boot是什么?

Spring Boot是流行的Web快速开发框架,使基于Spring的开发更便捷。
我们已经知道Spring很好用,而Spring Boot的设计目标是:

为一切Spring开发提供极速、通用的上手体验

开箱即用,但是当默认值不适合需求时不会妨碍你做改变

提供一组适用于各种项目类型的非功能性特性(如内嵌服务器、安全、度量、健康检查、外部配置)

完全不需要代码生成和XML配置

Kotlin + Spring Boot

Kotlin能轻松集成Spring Boot,用Java怎么写,用Kotlin基本上也怎么写。

Spring能在线生成项目,免去创建项目的烦恼,请猛击链接http://start.spring.io/ 。

我们用Gradle构建,写一个build.gradle文件:

</>复制代码

  1. buildscript {
  2. ext {
  3. springBootVersion = "1.3.5.RELEASE"
  4. kotlinVersion = "1.0.4"
  5. }
  6. repositories {
  7. mavenCentral()
  8. }
  9. dependencies {
  10. classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  11. classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
  12. }
  13. }
  14. apply plugin: "kotlin"
  15. apply plugin: "spring-boot"
  16. jar {
  17. baseName = "myapp"
  18. version = "0.1-SNAPSHOT"
  19. }
  20. sourceCompatibility = 1.8
  21. targetCompatibility = 1.8
  22. // class文件保留参数名称
  23. compileJava.options.compilerArgs.add "-parameters"
  24. compileTestJava.options.compilerArgs.add "-parameters"
  25. springBoot {
  26. mainClass = "myapp.ApplicationKt"
  27. }
  28. dependencies {
  29. compile "org.springframework.boot:spring-boot-starter-aop"
  30. compile "org.springframework.boot:spring-boot-starter-web"
  31. compile "org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}"
  32. }
先写一个主类Application.kt,放在src/main/kotlin目录下(自己想一个包名哈),来启动整个应用:

</>复制代码

  1. @SpringBootApplication
  2. open class Application {
  3. @Bean
  4. open fun json(): MappingJackson2JsonView {
  5. return MappingJackson2JsonView(ObjectMapper())
  6. }
  7. }
  8. fun main(args: Array) {
  9. SpringApplication.run(Application::class.java, *args)
  10. }

Kotlin的函数可定义在类外面,而特殊的main函数要么放在外面,要么放在伴生对象(companion object)里面。这里就放在外面吧!

你会发现class和fun前面有open修饰符,它的意思是非final,Kotlin默认一切都是final的,如果不想要final救要加上open。由于Spring有时要创建代理,要求类和方法不能为final,因此我们每一处都写上open,以免忘记。

这里只有一个json()方法,用来在Spring中初始化Jackson,这样我们就能使用JSON了。

现在来写一个RestController,提供RESTful API吧:

</>复制代码

  1. @RestController
  2. @RequestMapping("/api/users")
  3. open class UserApi {
  4. @RequestMapping("/{id}", method = arrayOf(RequestMethod.GET))
  5. open fun get(@PathVariable id: Long) = "User(id=$id, name=admin, password=123)"
  6. }

好简单啊!现在,在IDE中运行Application.kt文件,就开始运行了!用浏览器打开http://localhost:8080/api/users/1

现在要把数据保存到数据库了:

Spring Boot使用JPA非常简单(照着官网的getting started学吧),但我要介绍另一种ORM框架——Ebean,它模仿了Rails的Active Record,支持常用的JPA注解。值得一提的是,Ebean的作者也喜欢Kotlin。

需要一个配置文件src/main/resources/ebean.properties :

</>复制代码

  1. # 是否生成建表SQL
  2. ebean.db.ddl.generate=true
  3. # 是否执行建表SQL
  4. ebean.db.ddl.run=false
  5. datasource.db.username=DB用户名
  6. datasource.db.password=DB密码
  7. datasource.db.databaseUrl=jdbc:mysql://localhost:3306/你的database名称
  8. datasource.db.databaseDriver=com.mysql.jdbc.Driver

我们对ebean.db.ddl.run(是否执行建表SQL)选择了false。因为Ebean会生成建表SQL,我们可以手动执行,避免每次都重新建表,把数据丢弃了。编写实体类后再运行,SQL会生成在项目目录下,手动执行一下吧!(亦可在首次启动前把ebean.db.ddl.run改成true)

然后在Spring中初始化Ebean吧:

</>复制代码

  1. // 把这个方法添加到Application类
  2. @Bean(autowire = Autowire.BY_TYPE)
  3. open fun getEbeanServer(): EbeanServer {
  4. val config = ServerConfig()
  5. config.name = "db"
  6. config.loadFromProperties()
  7. config.isDefaultServer = true
  8. return EbeanServerFactory.create(config)
  9. }

然后要修改main方法,在Spring之前先执行Ebean的agent,改写实体类的字节码:

</>复制代码

  1. fun main(args: Array) {
  2. val packageName = "com.iostate.**" // 改成你自己的包名,实体类要放在这个包里面
  3. if (!AgentLoader.loadAgentFromClasspath("avaje-ebeanorm-agent",
  4. "debug=1;packages=$packageName")) {
  5. System.err.println(
  6. "avaje-ebeanorm-agent not found in classpath - not dynamically loaded")
  7. }
  8. SpringApplication.run(Application::class.java, *args)
  9. }

Ebean需要执行agent来改写字节码(instrumenation),而Hibernate则选择了给实体对象创建动态代理(dynamic proxy),都是为了能对实体进行AOP操作。

instrumenation使用复杂,调试简单;dynamic proxy使用简单,调试复杂。各有千秋,我更认同改写字节码。

编写实体类:

</>复制代码

  1. import javax.persistence.*
  2. import com.avaje.ebean.Model
  3. import com.avaje.ebean.annotation.WhenCreated
  4. import com.avaje.ebean.annotation.WhenModified
  5. import java.sql.Timestamp
  6. import com.avaje.ebean.annotation.SoftDelete
  7. import com.fasterxml.jackson.annotation.JsonIgnore
  8. @MappedSuperclass
  9. abstract class BaseModel : Model() {
  10. @Id @GeneratedValue
  11. var id: Long = 0
  12. @Version
  13. var version: Long = 0
  14. @WhenCreated
  15. var whenCreated: Timestamp? = null
  16. @WhenModified
  17. var whenModified: Timestamp? = null
  18. }
  19. @Entity
  20. class User (
  21. var name: String = "",
  22. @JsonIgnore
  23. var password: String = ""
  24. @SoftDelete
  25. var deleted: Boolean = false
  26. ) : BaseModel() {
  27. companion object find : Find()
  28. }

第一个类是所有实体模型的基类,提供一些通用字段。id是自增主键,version是乐观锁的标志,whenCreated是创建时间,whenModified是修改时间。有的变量类型以问号结尾,这个跟Swift语言是一样的,表示可为null(默认是非null的)。

第二类是User,行数很少,没有繁琐的getter/setter。@JsonIgnore的作用是防止敏感字段被泄露到JSON中,@SoftDelete的作用是软删除(数据不可见,但没有真的删除)。companion object find : Find()提供了一组快捷查询方法,如byId(id) all()

现在把UserApi修改如下:

</>复制代码

  1. @RestController
  2. @RequestMapping("/api/users")
  3. open class UserApi {
  4. @RequestMapping("/{id}", method = arrayOf(RequestMethod.GET))
  5. open fun get(@PathVariable id: Long) = User.byId(id)
  6. @RequestMapping("/new", method = arrayOf(RequestMethod.POST))
  7. open fun create(@RequestParam name: String, @RequestParam password: String): User {
  8. return User(name, password).apply {
  9. save()
  10. }
  11. }
  12. }

get方法真正向数据库做查询了!增加了create方法来创建用户!如果想用浏览器快速测试,把RequestMethod.POST改成GET,输入链接http://localhost:8080/api/users/new?name=admin&password=123 试试!

一个注意事项

Spring Boot能把程序打包成jar直接运行,这是很方便群众的!但是JSP和Ebean在jar模式都无法工作。
那么在生产环境要怎么解决呢?可以把jar解压运行!
参考文档的exploded archives: http://docs.spring.io/spring-...

</>复制代码

  1. # 解压
  2. unzip -q myapp.jar
  3. # 运行
  4. java org.springframework.boot.loader.JarLauncher
  5. # 生产模式用以下的nohup方式,以防程序随着shell一起关闭
  6. nohup java org.springframework.boot.loader.JarLauncher &

我自己用的命令不一样:

</>复制代码

  1. unzip -q myapp.jar
  2. nohup java -cp ".:./lib/*" com.myapp.ApplicationKt &

注意当前所在的工作目录,日志目录/logs会创建在当前工作目录下。

收工

我提供了一个示例项目,比较粗糙,请多多包涵 https://github.com/sorra/bms

老外也有几个示例项目,可供参考:

Spring Boot Kotlin project with a REST Webservice and Spring Data: https://github.com/sdeleuze/s...

Demo Webapp using SpringBoot, Kotlin and React.js: https://github.com/winterbe/s...

顺带一提,轻境界就是用Kotlin + Spring Boot构建的!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65227.html

相关文章

  • Kotlin + Spring Boot : 下一代 Java 服务开发

    摘要:下一代服务端开发下一代服务端开发第部门快速开始第章快速开始环境准备,,快速上手实现一个第章企业级服务开发从到语言的缺点发展历程的缺点为什么是产生的背景解决了哪些问题为什么是的发展历程容器的配置地狱是什么从到下一代企业级服务开发在移动开发领域 《 Kotlin + Spring Boot : 下一代 Java 服务端开发 》 Kotlin + Spring Boot : 下一代 Java...

    springDevBird 评论0 收藏0
  • SpringBoot 2.X Kotlin 系列之Hello World

    摘要:二教程环境三创建项目创建项目有两种方式一种是在官网上创建二是在上创建如图所示勾选然后点,然后一直默认最后点击完成即可。我们这里看到和普通的接口没有异同,除了返回类型是用包装之外。与之对应的还有,这个后面我们会讲到。 showImg(https://segmentfault.com/img/remote/1460000018819338?w=1024&h=500); 从去年开始就开始学习...

    warkiz 评论0 收藏0
  • SpringBootKotlin 完美交融

    摘要:环境依赖修改文件,添加依赖。使用为被标注的类去掉,允许被继承。数据源方案一使用默认配置使用默认配置,不需要在创建和的。相关为了展现效果,我们先定义一组简单的接口进行测试。 原文地址:梁桂钊的博客博客地址:http://blog.720ui.com 欢迎转载,转载请注明作者及出处,谢谢! 本文讲解 Spring Boot2 基础下,如何使用 Kotlin,并无缝整合与完美交融。为了让读...

    golden_hamster 评论0 收藏0
  • 初探Kotlin+SpringBoot联合编程

    摘要:是一门最近比较流行的静态类型编程语言,而且和一样同属系。这个生成的构造函数是合成的,因此不能从或中直接调用,但可以使用反射调用。 showImg(https://segmentfault.com/img/remote/1460000012958496); Kotlin是一门最近比较流行的静态类型编程语言,而且和Groovy、Scala一样同属Java系。Kotlin具有的很多静态语言...

    xiaokai 评论0 收藏0
  • 使用kotlin开发最新版的spring boot应用 | SpringBoot实践

    摘要:官方定义项目介绍长期目标是使用开发一个完整的应用,目前搭建了最小量的脚手架。测试程序和都提供了强大的测试套件,能够很好的和集成,进行集成测试和单元测试。编写一些基本的集成测试,检查接口是否正常。 showImg(https://segmentfault.com/img/remote/1460000008751790); kotlin是由IntelliJ IDEA的开发商Jetbrain...

    mj 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<