领先一步
VMware 提供培训和认证,助您加速进步。
了解更多更新:现已提供一份全面的 Spring Boot + Kotlin 教程。
继我们几个月前在 start.spring.io 上推出 Kotlin 支持 之后,我们继续努力确保 Spring 和 Kotlin 能够良好地协同工作。Kotlin 的主要优势之一是它与 Java 编写的库具有非常好的 互操作性。但是,还有更进一步的方法,可以在开发下一个 Spring 应用程序时编写完全惯用的 Kotlin 代码。除了 Spring Framework 对 Java 8 的支持(Kotlin 应用程序可以利用其函数式 Web 或 Bean 注册 API)之外,还有专门针对 Kotlin 的附加功能,它们应该能让您达到新的生产力水平。
因此,我们在 Spring Framework 5.0 中引入了专门的 Kotlin 支持,我将在本博文中总结这些功能,这些功能旨在让您在将这些技术结合使用时获得无缝的开发体验。您可以使用 此链接在 Spring Framework 错误跟踪器中查找与 Kotlin 相关的问题。
我们 Kotlin 支持的一个关键构建块是 Kotlin 扩展。它们允许以非侵入性的方式扩展现有 API,从而为实用工具类或特定于 Kotlin 的类层次结构提供更好的替代方案,以向 Spring 添加特定于 Kotlin 的功能。像 Mario Arias 的 KotlinPrimavera 这样的库已经展示了我们可以为 Spring API 带来的各种 Kotlin 助手,以便编写更惯用的代码。通过 Spring Framework 5,我们将最有用和最受欢迎的扩展集成到 Spring Framework 依赖项中,并且我们还在添加新的扩展!请注意,Kotlin 扩展是静态解析的,您需要导入它们(类似于 Java 中的静态导入)。
## Spring Framework API 的空安全
Spring Framework 5.0 为所有包引入了正式的非空 API 声明,现在将明确可空的参数和返回值进行了相应注解。我们的可空性注解符合 JSR 305 标准,并且一旦 KT-10942 修复,Kotlin 将会支持它们。这将使整个 Spring Framework API 从 Kotlin 端实现空安全,并允许在编译时处理空值,而不是在运行时抛出 NullPointerExceptions。
## 在 Spring 注解中利用 Kotlin 的可空信息
Spring 最初基于 Raman Gupta 的社区贡献,现在利用 Kotlin 的空安全支持 来确定 HTTP 参数是否是必需的,而无需显式定义 required 属性。这意味着 @RequestParam name: String? 将被视为非必需,而 @RequestParam name: String 将被视为必需。Spring Messaging 的 @Header 注解也支持此功能。
类似地,使用 @Autowired 或 @Inject 的 Spring Bean 注入利用此信息来了解 Bean 是否是必需的。@Autowired lateinit var foo: Foo 暗示应用程序上下文中必须注册一个类型为 Foo 的 Bean,而 @Autowired lateinit var foo: Foo? 在不存在此类 Bean 时不会引发错误。
## Spring WebFlux 函数式 DSL
Spring Framework 5.0 附带了一个 Kotlin 路由 DSL,它允许您利用最近发布的 Spring 函数式 Web API,并用简洁惯用的 Kotlin 代码编写。
router {
("/blog" and accept(TEXT_HTML)).nest {
GET("/", fooHandler::findAllView)
GET("/{slug}", fooHandler::findOneView)
}
("/api/blog" and accept(APPLICATION_JSON)).nest {
GET("/", barHandler::findAll)
GET("/{id}", barHandler::findOne)
}
}
感谢 Yevhenii Melnyk 的早期原型和帮助!您可以在 https://github.com/mixitconf/mixit/ 的 Spring Boot 应用程序的实际示例 中看到使用函数式 Web API 的例子。
## 函数式 Bean 声明 DSL
Spring Framework 5.0 引入了一种新的 Bean 注册方式,使用 lambda 作为 XML 或 @Configuration 和 @Bean 的 JavaConfig 的替代方案。简而言之,它使得使用充当 FactoryBean 的 Supplier lambda 来注册 Bean 成为可能。
在 Java 中,您可能会这样写
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Foo.class);
context.registerBean(Bar.class, () -> new
Bar(context.getBean(Foo.class))
);
而在 Kotlin 中,可重构类型参数和 函数式 Bean 声明 DSL 使我们能够简单地这样写
beans {
bean<Foo>()
bean { Bar(ref()) }
}
可用的 ApplicationContext 相关 Kotlin 扩展包括 BeanFactoryExtensions、ListableBeanFactoryExtensions、GenericApplicationContextExtensions 和 AnnotationConfigApplicationContextExtensions。
## RestTemplate 和 WebClient API 的扩展
例如,Kotlin 的可重构类型参数为 JVM 的 泛型类型擦除提供了变通方法,因此我们引入了一些扩展来利用此功能,从而在可能的情况下提供更好的 API。
这允许为 RestTemplate(例如,感谢 Netflix 的 Jon Schneider 的贡献)和新的 WebClient Spring WebFlux API 提供方便的 API。例如,在 Java 中检索 Foo 对象列表需要这样写:
Flux<User> users = client.get().retrieve().bodyToFlux(User.class)
而在 Kotlin 中,使用 Spring Framework 5 扩展,您将能够这样写:
val users = client.get().retrieve().bodyToFlux<User>()
// or (both are equivalent)
val users : Flux<User> = client.get().retrieve().bodyToFlux()
与 Java 一样,在 Kotlin 中,users 是强类型的,但 Kotlin 巧妙的类型推断允许更简洁的语法。
Spring Framework 5.0 中可用的 Web API Kotlin 扩展包括 RestOperationsExtensions、ServerRequestExtensions、BodyInsertersExtensions、BodyExtractorsExtensions、ClientResponseExtensions、ModelExtensions 和 ModelMapExtensions。
值得注意的是,像 Spring Data MongoDB 这样的其他 Spring 项目也通过这些扩展为 Kotlin 提供了内置支持。
## Reactor Kotlin 内置支持
Reactor 是 Spring Framework 5.0 构建在其上的响应式基础,在开发响应式 Web 应用程序时,您很有可能使用其 Mono、Flux 和 StepVerifier API。
因此,今天我们还在即将发布的 Reactor 3.1 版本中引入了 Kotlin 内置支持!它提供了扩展,能够通过编写 foo.toMono() 从任何类实例创建 Mono 实例,许多人会更喜欢这种方式而不是 Mono.just(foo)。它还支持例如从 Java 8 Stream 实例创建 Flux,使用 stream.toFlux()。此外,还提供了 Iterable、CompletableFuture 和 Throwable 扩展,以及基于 KClass 的 Reactor API 变体。
## 不再需要将 Bean 类声明为 open
到目前为止,使用 Kotlin 构建 Spring Boot 应用程序时遇到的少数痛点之一是需要为 CGLIB 代理的 Spring Bean(如 @Configuration 类)的每个类及其成员函数添加 open 关键字。此要求的根本原因是,在 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 的这篇博文 以获取更多详细信息,包括与 Spring Data 实体一起使用的 kotlin-jpa 和 kotlin-noarg 插件。
## 基于 Kotlin 的 Gradle 构建配置
早在五月份,Gradle 就 宣布 除了 Groovy 之外,还将支持使用 Kotlin 编写构建和配置文件。这使得在 IDE 中可以获得完整的自动完成和验证功能,因为这些文件是常规的静态类型 Kotlin 脚本文件。这很可能成为基于 Kotlin 的项目的自然选择,但对于 Java 项目也同样有价值。
自五月以来,kotlin-dsl Gradle 项目一直在不断发展,现在可以使用,但要注意 2 个警告:
spring-boot-kotlin-demo 和 mixit 项目都使用了这样的 Kotlin Gradle 构建,因此请随意查看。我们正在 讨论 在 start.spring.io 上添加此类支持。
## 基于 Kotlin 脚本的模板
从 4.3 版本开始,Spring Framework 提供了一个 ScriptTemplateView 来渲染支持 JSR-223 的脚本引擎的模板,Spring Framework 5.0 更进一步,支持 i18n 和嵌套模板。Kotlin 1.1 提供了此类支持,并允许渲染基于 Kotlin 的模板,有关详细信息,请参阅 此提交。
这使得一些有趣的用例成为可能,例如使用 kotlinx.html DSL 或简单地带有插值的 Kotlin 多行 String 来编写类型安全的模板,如这个 kotlin-script-templating 项目所示。这可以让你编写这类模板,并在 IDE 中获得完整的自动完成和重构支持。
import io.spring.demo.*
"""
${include("header")}
<h1>${i18n("title")}</h1>
<ul>
${users.joinToLine{ "<li>${i18n("user")} ${it.firstname} ${it.lastname}</li>" }}
</ul>
${include("footer")}
"""
## 结论
我用 Kotlin 编写 Spring Boot 应用程序越多,我就越觉得这两种技术有相同的理念,并能让你编写更高效的应用程序,代码富有表现力、简洁且易于阅读,而 Spring Framework 5 的 Kotlin 支持是朝着自然、简单而强大的方式结合这些技术迈出的重要一步。
Kotlin 可以用于编写 基于注解的 Spring Boot 应用程序,并且也同样适用于 Spring Framework 5.0 将实现的 函数式和响应式应用程序 的新类型。
Kotlin 团队在修复我们报告的几乎所有痛点方面都做得非常出色,因此非常感谢他们。即将发布的 Kotlin 1.2 版本预计还将修复 KT-11235,以便在不使用 arrayOf() 的情况下为数组注解属性指定单个值。您可能面临的主要剩余问题是 KT-14984,它需要明确指定 lambda 类型,而仅仅指定 { } 应该就足够了。
请通过访问 start.spring.io 并生成一个 Spring Boot 2.0.0 (里程碑或快照) 项目来测试 Spring Framework 5.0 的 Kotlin 支持,并在此处或 Kotlin Slack 的 #spring 频道中向我们提供反馈。您也可以 贡献 您需要的 Kotlin 扩展 ;-)