使用 Kotlin 开发 Spring Boot 应用

工程 | Sébastien Deleuze | 2016 年 2 月 15 日 | ...

更新:现已提供一份全面的 Spring Boot + Kotlin 教程

恰逢 Kotlin 1.0 发布,我们在 https://start.spring.io 中增加了对 Kotlin 语言的支持,以便更轻松地使用该语言启动新的 Spring Boot 项目。

这篇博文也给了我一个机会,解释为什么我认为这种语言很有趣,向您详细展示一个示例项目,并为您提供一些技巧。

Kotlin 是什么?

Kotlin 是由 JetBrains 创建的一种语言。它运行在 JVM 之上(但不仅限于 JVM),它是一种面向对象的语言,融合了许多函数式编程的思想。我不会过多地介绍 Kotlin 的所有特性(PDFHTML),但我希望重点介绍我发现最有趣的那些特性。

  • Kotlin 是一种静态类型语言,但得益于其 智能类型推断,它允许您像使用动态语言一样编写简洁且富有表现力的代码,同时性能接近纯 Java 项目。
  • 属性支持
  • 与其他语言相比,标准库相对轻量
  • 易于学习:Java 开发人员可以快速理解该语言的大部分内容(这篇 与 Java 的快速对比 值得一读)。
  • Java 互操作性是首要的,并且非常出色。
  • 非常适合 Android 开发
  • 内置的不变性和 空安全 支持
  • 代码易于阅读,编写效率高
  • 允许 扩展现有库,而无需继承该类或使用任何类型的设计模式(如装饰器模式)。
  • 不需要分号 ;-)

您可以在这篇 Kotlin 2015 摘要 的博文中找到许多有助于提高 Kotlin 知识的有用链接。还可以看看这些简单的 Kotlin 练习,快速了解该语言。

Spring Boot + Kotlin 项目示例

Kotlin 的设计旨在与 Java 生态系统良好协作,在我看来,它与 Spring Boot 拥有相同的务实、创新和固执己见的理念,因此它们可以很好地协同工作。您可以查看这个简单的 Spring Boot + Spring Data JPA Kotlin 项目,更具体地了解它的样子。

Kotlin 凭借其非常简洁的 声明语法,可以轻松编写(和阅读)您的领域模型。您可以看到 Kotlin 允许您指定参数的默认值,并且类型在变量/参数名称之后声明。

@Entity
class Customer(
	var firstName: String = "",
	var lastName: String = "",
	@Id @GeneratedValue(strategy = GenerationType.AUTO)
	var id: Long = 0
)

下面看到的 Spring MVC REST 控制器使用了构造函数注入,并且 Kotlin 中的默认可见性是 public,因此无需指定。当函数返回单个表达式时,可以省略花括号,并且主体在 = 符号之后指定。这更好,因为编译器可以推断出返回类型。

@RestController
class CustomerController (val repository:CustomerRepository) {

	@GetMapping("/")
	fun findAll() = repository.findAll()

	@GetMapping("/{name}")
	fun findByLastName(@PathVariable name:String)
		= repository.findByLastName(name)
}

Spring Data 存储库不言自明。

interface CustomerRepository : CrudRepository<Customer, Long> {
	fun findByLastName(name: String): List<Customer>
}

由于 Kotlin 支持顶级函数,您可以像这样简单地声明您的应用程序:

@SpringBootApplication
class Application {

	@Bean
	fun init(repository: CustomerRepository) = CommandLineRunner {
		repository.save(Customer("Jack", "Bauer"))
		repository.save(Customer("Chloe", "O'Brian"))
		repository.save(Customer("Kim", "Bauer"))
		repository.save(Customer("David", "Palmer"))
		repository.save(Customer("Michelle", "Dessler"))
	}
}

fun main(args: Array<String>) {
	SpringApplication.run(Application::class.java, *args)
}

您需要使用 kotlin-spring 插件 来自动使 @Configuration 类以及其他一些 @Service@Repository 成为 open,因为它们在 Spring 中不能是 final 的,这归因于 CGLIB 代理的使用(Kotlin 中的类和方法默认是 final 的,没有 open 修饰符)。使用 JDK 动态代理的 Bean 不需要 open 修饰符。

附加提示

尽管 Spring Boot 和 Kotlin 配合得很好,但以下附加提示也值得了解。请参阅 此 issue 了解有关改进 Spring Boot 中 Kotlin 支持的更多详细信息。

数组注解属性

与 Java 不同,Kotlin 目前不允许将数组注解属性指定为单个值(value 属性除外),因此请注意,您必须编写 @RequestMapping(method = arrayOf(RequestMethod.GET))@EnableAutoConfiguration(exclude = arrayOf(Foo::class))

预计此行为将在即将发布的 Kotlin 1.2 中得到改进(有关更多详细信息,请参阅此 Kotlin issue)。Spring Framework 4.3 的复合注解(如 @GetMapping@PostMapping)也可以提供帮助。

属性注入

我们之前已经看到了如何进行构造函数注入,因为这是推荐的方法(尤其是在 Kotlin 中)。如果您必须执行属性注入,您将不得不使用延迟初始化属性,因为通常,声明为非可空类型的原始属性必须在构造函数中初始化。

@RestController
class CustomerController {

	@Autowired
	lateinit var repository:CustomerRepository

	// ...
}

属性占位符

在 Kotlin 中,$ 用于字符串插值,因此在使用属性占位符时应对其进行转义:@Value("\${some.property}")。作为替代,您也可以使用@ConfigurationProperties,有关更多详细信息,请参阅此Stack Overflow答案。

Jackson Kotlin 模块

如果您使用 Jackson,您可能希望添加 com.fasterxml.jackson.module:jackson-module-kotlin 依赖项,以便它能够处理没有默认构造函数或使用 Kotlin 集合的数据类。Spring Framework 4.3+ 会自动注册它。

尝试使用 Java 转 Kotlin 转换器

最后一个提示,IntelliJ IDEA 中的 Java 转 Kotlin 转换器(菜单 Code -> Convert Java file to Kotlin file)在您无法弄清楚如何在 Kotlin 中编写某些内容时非常有用。因此,请不要犹豫,先用 Java 编写一些内容,然后使用它来查找 Kotlin 的对应项。此与 Java 的对比文档也可以提供一些帮助。

反馈

我们对您使用 Kotlin 开发 Spring 应用程序的反馈很感兴趣。这篇博文只是一个介绍,还有很多内容可以讨论,尤其是在使用 Spring Boot 和更具 Kotlin 风格的代码(如使用 Exposed SQL 库)方面,敬请期待 **(更新:下一篇 Kotlin 博文现已发布 在此)**……

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有