转载

Spring Framework 5.0 对 Kotlin 支持的介绍

继几个月前公布 Kotlin 已在 start.spring.io 中受支持 后,我们继续努力以实现 Spring 和  Kotlin 的更好共存。Kotlin 的一个重要的特性是能与 Java 库很好地 互用 ,但想要在 Spring 中编写原汁原味的 Kotlin 代码,还有一段路要走。除了 Spring 对 Java 8 的支持让 Kotlin 受益(比如说函数式 Web 编程或是 bean 注册 API)之外,还应该有一些专为 Kotlin 准备的特性来让你的生产力提升一个档次。

这就是为什么我们在 Spring Framework 5.0 M4 上专门为 Kotlin 引进支持。我在这篇博客中对这些特性做了一下总结,好让你更顺滑地结合 Spring 和 Kotlin 使用。如果在过程中遇到问题,你能在 这里 找到 Spring bug 跟踪系统里与 Kotlin 相关的 issue。

我们对 Kotlin 支持中的一个关键构造块就是 Kotlin 扩展 。它们能对现有的 API 实现非侵入式的扩展,为利用类或者 Kotlin 的特殊类层级结构,以此提供一个更好的可选方案来向 Spring 加入 Kotlin 的专有功能特性。一些像来自于  Mario Arias 的 KotlinPrimavera 的库,已经展示了各种各样的 Kotlin 辅助程序,我们可以将它们引入到 Spring API , 从而写出更加优雅的代码。运用 Spring Framework 5,我们会在 Spring 框架依赖中集成最实用且最流行的扩展,而如今我们又多了一个新帮手!要注意 Kotlin  的扩展是要进行静态解析的,所以你先将它们导入(就像 Java 中的静态导入一样)。

使用 Kotlin 进行功能性的 Bean 注册

Spring Framework 5.0 引入了一种注册 Bean 的新方法,作为利用 XML 或者 JavaConfig 的 @Configuration 或者 @Bean 的替代方案。简言之,它能实现用供应器 lambda 扮演工厂 Bean 的角色。

例如用 Java 代码你会这样写:

GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Foo.class);
context.registerBean(Bar.class, () -> new 
	Bar(context.getBean(Foo.class))
);

而使用 Kotlin, 因为被具象化的类型参数,我们可以将代码写成这样:

val context = GenericApplicationContext {
    registerBean<Foo>()
    registerBean { Bar(it.getBean<Foo>()) }
}

你可以在 https://github.com/mix-it/mixit/ 看到一个同时使用了  web 和  bean 注册 API 的 Spring 应用程序的具体示例。

跟 Kotlin 扩展相关的可用 ApplicationContext 有 BeanFactoryExtensions , ListableBeanFactoryExtensions ,  GenericApplicationContextExtensions 以及  AnnotationConfigApplicationContextExtensions 。

如何用 Kotlin 的方式使用 Spring Web 的功能性 API

Spring Framework 5.0 带来的 RouterFunctionDsl 可以让你使用干净且优雅的 Kotlin 代码来运用最近才推出的 Spring 功能性 Web API :

fun route(request: ServerRequest) = RouterFunctionDsl {
    accept(TEXT_HTML).apply {
            (GET("/user/") or GET("/users/")) { findAllView() }
            GET("/user/{login}") { findViewById() }
    }
    accept(APPLICATION_JSON).apply {
            (GET("/api/user/") or GET("/api/users/")) { findAll() }
            POST("/api/user/") { create() }
            POST("/api/user/{login}") { findOne() }
    }
 } (request)

利用 Kotlin 可为空(nullable) 信息

最初基于一项来自于 Raman Gupta 的社区贡献, Spring 现在将其拿过来加以利用,实现了  Kotlin 对空值安全性的支持, 以此来判断一个 HTTP 参数是否是必需的,但不用去对必需的属性进行明确的定义了。这就意味着 @RequestParam name: String? 会被看成是非必需,而 @RequestParam name: String 是必需的意思。这个也在 Spring Messaging 上的 @Header 注解中得到了支持。

同样以类似的方式,Spring 使用 @Autowired 或者 @Inject 所进行的 Bean 注入也利用了这一信息来获悉一个 Bean 是否是必需的。@Autowired lateinit var foo: Foo 表示一个类型为 Foo 的 Bean 必需在应用程序上下文中被注册,而 @Autowired lateinit var foo: Foo? 则在这样一个 Bean 不存在的时候不会发生错误。

RestTemplate 和功能性 Web API 扩展

例如, Kotlin 被具象化的类型参数 提供了一种用于 JVM  泛型类型擦除 的方法, 因此我们引入了一些扩展来对该特性加以利用,以尽可能提供一个更好的 API。

这样就能实现提供用于 RestTemplate 的便利 API (感谢 Netflix 的 Jon Schneider 对此的贡献)。例如,要提取一个 Foo 对象的列表,用 Java 代码你要这样写:

List<Foo> result = restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference<List<Foo>>() { }).getBody();

或者如果你使用了一个中间人数组,就用这种方式写:

List<Foo> result = Arrays.asList(restTemplate.getForObject(url, Foo[].class));

而使用了 Spring Framework 5 扩展,你可以能够用 Kotlin 来这样写:

val result: List<Foo> = restTemplate.getForObject(url)

Spring Framework 5.0 中可用的 Web API Kotlin 扩展有 RestOperationsExtensions , ServerRequestExtensions ,  BodyInsertersExtensions ,  BodyExtractorsExtensions , ClientResponseExtensions ,  ModelExtensions 以及  ModelMapExtensions 。

这些扩展也提供了支持本地 Kotlin KClass 的成员函数,让你可以指定 Foo::class 参数而不是 Foo::class.java。

Reactor Kotlin 扩展

Reactor 是 Spring Framework 5.0 附带的响应式框架, 而在开发一个响应式 web 应用程序时,有不少可以利用上  Mono ,  Flux 和  StepVerifier API 的好机会。

因此今天我们也要藉由新的 reactor-kotlin 项目,来对 Reactor 中的 Kotlin 支持进行一下介绍! 它提供了能够通过写上 writingfoo.toMono() 来从任何类实例创建出 Mono 实例的扩展, 许多人会喜欢写成 Mono.just(foo)。它也支持诸如利用 stream.toFlux() 来从一个 Java 8 Stream 实例创建出一个 Flux。Iterable, CompletableFuture 以及 Throwable 扩展,还有基于 KClass 的 Reactor API 变体也得到了提供。

现在这个项目仍处于早期阶段, 因此如果你感觉这个项目还缺了点什么,请随意 贡献 出你自己的扩展吧。

类声明不再需要 open

到现在为止,使用 Kotlin 构建 Spring Boot 应用程序时遇到了几个问题,其中之一就是需要在每个类上添加一个 open 关键字,并且使用 CGLIB(如 @Configuration 类)代理它们的成员函数。这个需求的根本原因在于 Kotlin 的类是 默认为 final 。

幸运的是,Kotlin 1.0.6 现在提供了一个 kotlin-spring 插件,可以用以下注解或元注解打开类或成员函数:

  • @Component

  • @Async

  • @Transactional

  • @Cacheable

元注解支持意味着使用 @Configuration,@Controller,@RestController,@Service 或 @Repository 注解的类会自动打开,因为这些注解使用了@Component。

我们更新了 start.spring.io 以在默认情况下启用。你可以阅读  Kotlin 1.0.6 博客 来了解更多细节,如新的 kotlin-jpa 和 kotlin-noarg 插件。

基于 Kotlin 的 Gradle 构建配置

回到5月份,Gradle 宣布 他们将支持在 Groovy 和 Kotlin 中编写构建和配置文件。这使得在 IDE 中实现自动完成和验证成为可能,因为这些文件通常是常规静态类型的 Kotlin 脚本文件。这很可能成为基于 Kotlin 的项目的自然选择,这对 Java 项目也有很大价值。

自5月以来, gradle-script-kotlin 项目不断发展,但在使用前需要谨记以下两点:

  • 你需要使用 Kotlin 1.1-EAP IDEA 插件来支持自动完成功能(但如果你正在使用 kotlin-spring 插件,就需等待 Kotlin 1.1-M05,因为 1.1-M04 不能稳定地使用该插件)

  • 文档还不完善,但你可以在 Kotlin Slcak 上的 #gradle 频道中找 Gradle 团队寻求帮助。

Spring-boot-kotlin-demo 和  mixit 项目都是通过基于 Kotlin 的 Gradle 版本构建的,有兴趣可以看看。我们正在 考虑 在 start.spring.io 中添加这种支持。

基于模板的 Kotlin 脚本

从 4.3 版本开始,Spring Framework 提供了一个 ScriptTemplateView ,用于利用支持  JSR-223 的脚本引擎来渲染模板。 Kotlin 1.1-M04 提供了这样的支持,并支持渲染基于 Kotlin 的模板,更为详细信息,请参阅 提交记录 。

随之也产生了一些有趣的案例,如使用 kotlinx.html DSL 编写类型安全模板或简单地使用 Kotlin 多 String 字符串插值,正如  kotlin-script-templating 项目中所示。你可以在 IDE 中编写具有自动完成功能和重构支持的一类模板:

import io.spring.demo.User
import io.spring.demo.joinToLine

"""
${include("header", bindings)}
<h1>Title : $title</h1>
<ul>
    ${(users as List<User>).joinToLine{ "<li>User ${it.firstname} ${it.lastname}</li>" }}
</ul>
${include("footer")}
"""

这个功能仍在开发中,但我正在与 Kotlin 团队合作,以确保在 MVC 和 Spring Framework 5.0 GA 的 Reactive 中暂时能够与嵌套模板和支持 i18n 的功能一起工作。

总结

用 Kotlin 写 Spring Boot 应用越多,我越是感到这两种技术具有相同的技术理念,它们能够让你写出更具有表现力、更加简洁、易读性更强的代码,从而更加有效地去进行开发工作,Spring Framework 5 对 Kotlin 的支持,更是自然、简洁、有效地整合这些技术的重要一步。

Kotlin 可以用来写 基于注解的 Spring Boot 应用 ,同时也能够很好地和 Spring  Framework 5.0 中支持的新型 功能性和反应性应用 兼容。

Kotlin 工作组基本上都解决了我们反馈的问题,感谢他们的努力。马上就要发布的 Kotlin1.1 版本也将修改 KT-11235 ,以便允许指定阵列不使用 arrayof()就能赋予单值属性。主要遗留问题可能是 KT-4984 ,它要求指定特定的 lambda 类型,但其实你只需要指定一个 {} 就够了。

欢迎到 start.spring.io 建立一个 Spring Boot 2.0.0 项目来体验 Spring Framework 5.0 对 Kotlin 的支持,您可以将您的反馈发送到这里,或者是发送到 Kotlin Slack 的 #spring 频道。您也可以将您需要的 Kotlin 扩展 投稿 。

原文  https://www.oschina.net/translate/introducing-kotlin-support-in-spring-framework-5-0
正文到此结束
Loading...