使用 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`,因为由于 CGLIB 代理的使用,它们在 Spring 中不能是`final`(Kotlin 中的类和方法默认为`final`,没有`open`修饰符)。使用 JDK 动态代理的 Bean 不需要`open`修饰符。

其他提示

即使 Spring Boot 和 Kotlin 可以很好地协同工作,这些额外的提示也值得了解。有关详细信息,请参阅关于改进 Spring Boot 中 Kotlin 支持的此问题

数组注解属性

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

此行为有望在即将推出的 Kotlin 1.2 中得到改进(有关详细信息,请参阅此 Kotlin 问题)。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 转换器(菜单 代码 -> 将 Java 文件转换为 Kotlin 文件)在你不知道如何用 Kotlin 编写某些内容时非常有用。因此,不要犹豫,先用 Java 编写一些代码,然后用它来查找 Kotlin 的对应代码。这份与 Java 的对比文档也可以提供一些帮助。

反馈

我们非常关注您关于使用 Kotlin 开发 Spring 应用程序的反馈。这篇博文只是一个介绍,还有很多内容需要讲述,特别是关于使用 Spring Boot 和更符合 Kotlin 风格的代码(例如使用Exposed SQL 库),敬请关注(更新:下一篇 Kotlin 博文现已上线……

获取 Spring Newsletter

关注 Spring Newsletter

订阅

领先一步

VMware 提供培训和认证,助您快速提升。

了解更多

获取支持

Tanzu Spring 通过简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

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

查看全部