转载

从Java 8到Kotlin的Spring Boot应用程序之旅

Categories:    Spring Boot      Kotlin    

从Java 8到Kotlin的Spring Boot应用程序之旅

在写了很多年的Ruby / Rails之后,最近我发现自己写了大量的Spring Boot应用程序。 Spring Boot 是JVM的一个很好的框架,它通过“使创建“可以轻松运行”的独立的,生产级的基于Spring的应用程序变得容易”来关注开发人员的工作效率。它具有许多Rails的感觉。“常规配置”部门,但是由于我最终使用Java 8,因此我失去了用Ruby编写时获得的“乐趣”。尽管Java 8在Java 7方面进行了重大改进,但我还是想知道通过使用 Kotlin 编写Spring Boot应用程序 ,我可以获得更多的喜悦

Kotlin是 JetBrains的 一种新语言,它 是IntelliJ和RubyMine的创建者,可以代替Java开发其产品。 他们的目标是创建一种更加简洁的基于JVM的语言,该语言有助于提高开发人员的工作效率,避免Java开发中的一些常见陷阱并与现有Java程序100%兼容。 它以Java 6为基准,同时仍添加了一些出色的语言功能,因此对Android开发也非常有用。

这篇文章以及所有后续文章将以现有的Java 8 / Spring Boot  应用程序 作为探索的起点。 这将使我看到Java 8语法和Kotlin语法之间的直接比较。 这次旅行将使我能够亲身体验Spring Boot / Kotlin应用程序的外观,并在学习过程中学习比“ Hello World”应用程序还多的语言。 如果您想在旅途中继续前进,可以在 GitHub上查看 不断发展的源代码

此外,这些帖子并不意味着要成为有关Kotlin的完整教程,而仅涵盖与转换有关的语言功能。 如果您需要完整的教程,则Kotlin网站上有很多 很好的信息

最后,如果您 对代码 有任何改进建议 ,请随时提交GitHub问题或提交拉取请求。

起跑线

启动Spring Boot应用程序时,我们需要的第一件事是应用程序类。 这是 我开始 应用程序类

package com.example.billing;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker

public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

...

这里没有惊喜。 main() 当您运行可执行jar文件时,我们在Spring Boot检测到的Application类上 创建一个静态 方法。

这是Kotlin中的同一应用程序类:

package com.example.billing

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker
import org.springframework.cloud.client.discovery.EnableDiscoveryClient

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker

// This class must not be final or Spring Boot is not happy.
open class Application {
    companion object {
        @JvmStatic fun main(args: Array<String>) {
            SpringApplication.run(Application::class.java, *args)
        }
    }
}

您可能会注意到的第一个区别是缺少分号。 是的,女士们,先生们,在科特林没有分号。 尽管对某些人来说不是什么大不了的事,但这对我来说是朝正确方向迈出的一步。

我注意到的下一个区别是 open 类定义前面 关键字。 默认情况下,Kotlin中的类是最终类,这是根据 Effective Java 继承的设计和文档中的 第17项, 否则将禁止使用 这是我第一次在Kotlin的“强制执行良好做法”与Spring Boot的约定之间产生摩擦。 @SpringBootApplication 是一个方便的注释,标记与类 @Configuration @EnableAutoConfiguration @ComponentScan 注释。 正是 @Configuration 注解强制使用 open 关键字。 open 在应用程序启动时, 删除 关键字会导致运行时错误:

org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Configuration class 'Application' may not be final. Remove the final modifier to continue.

由于此应用程序类不包含任何配置信息,因此修复起来很容易。 而不是使用的 @SpringBootApplication 标注可以 替代 @EnableAutoConfiguration @ComponentScan 注释。

package com.example.billing

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker
import org.springframework.cloud.client.discovery.EnableDiscoveryClient

@EnableAutoConfiguration
@ComponentScan
@EnableDiscoveryClient
@EnableCircuitBreaker

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

我注意到的最终差异在于 main() 方法 的定义 Kotlin有一个 伴随对象 的想法 这些对象的使用方式类似于Java中的静态方法,但不完全相同。 这就是 @JvmStatic 注释的来源。该注释告诉Kotlin生成实际的Java静态方法,而不是Kotlin中默认的“ kinda,sorta”方法。 此注释是对JVM兼容性进行投资的一个很好的例子。

main() 方法也缺少 public 修饰符。 默认情况下 方法 在Kotlin中 是公共的 ,这减少了Java应用程序中存在的样板。

最后,您会注意到Kotlin中的数组是实际的参数化类,而不是Java中的原始类型。 Kotlin还在变量定义后放置类型注释。 我们将在以后的帖子中探讨为什么这很重要。

Kotlin应用程序类的最后一个难题是,您必须告诉Spring Boot在哪里可以找到应用程序类。 在Gradle中,就像这样简单:

springBoot {
    mainClass = 'com.example.billing.Application'
}

备用应用程序类

Kotlin还允许在类之外定义函数,因此我们可以将应用程序类重写为:

package com.example.billing

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
import org.springframework.context.annotation.ComponentScan

@EnableAutoConfiguration
@ComponentScan
@EnableDiscoveryClient
@EnableCircuitBreaker

class Application

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

如果执行此操作,则该 main() 方法在名为的类上定义,该类 ApplicationKt 以file命名 Application.kt ,这会稍微改变 build.gradle 条目:

springBoot {
mainClass = 'com.example.billing.ApplicationKt'
}

此定义 main() 稍微 简化了 方法 的签名 注释和显式的伴随对象已经一去不复返了,因此代码也变得更加整洁。

我不确定我更喜欢哪一个。 使用伴随对象可以更明确地说明哪个类包含该 main() 方法,但是上面的定义更为简洁。 在这里,我们以较少的代码交换来隐式理解编译器将生成ApplicationKt类。 随着时间的流逝,我认为简化的应用程序类将在我身上发展。

总结思想

从Java 8到Kotlin的Spring Boot应用程序之旅

在我看来,Kotlin作为“更好的Java”朝正确的方向迈出了一步。 在我看来,语言设计师已尽其所能保持与现有Java程序的兼容性,同时又不受Java遗留的束缚。 缺少半冒号似乎是微不足道的,但会在大型代码库中加起来,并且在语言级别实施最佳实践也将有助于大型代码库。

是的,在平稳地与Spring Boot集成方面存在一些小难题,但是新语法和语言结构的好处远远超过了这些难题。

在我们 在下一篇文章中 ,我们将看看Java的 Spring Boot 配置类,并将它们与自己的兄弟科特林。 我希望我们将继续看到Kotlin带来的收益将超过Spring Boot带来的摩擦。

本系列 第一篇文章 中,我们研究了Spring Boot应用程序类从Java 8到Kotlin的转换。 这些迁移的好处是,由于Kotlin与旧版Java的结合很好,因此可以逐步完成它们。 实际上,这是该语言的设计考虑因素之一。

在第二篇文章中,我们将研究配置类到Kotlin的转换。

这是用Java 8编写的Spring Boot配置类的示例:

package com.example.billing;

import com.example.billing.reocurringPayments.Service;
import com.example.payments.RecurlyGateway;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
class Configuration {
    @Bean
    public com.example.payments.Gateway paymentGateway() {
        return new RecurlyGateway();
    }

    @Bean
    public Service serviceThatMayFail() {
        return new Service();
    }
}

这是用Kotlin编写的同一配置类:

package com.example.billing

import com.example.billing.reocurringPayments.Service
import com.example.payments.RecurlyGateway
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
open class Configuration {
    @Bean
    open fun paymentGateway() = RecurlyGateway()

    @Bean
    open fun serviceThatMayFail() = Service()
}

没有很多巨大的差异,但以下是一些对我来说比较小的差异:

  • 必须将Configuration类声明为打开。 这是因为Spring Boot继承了您的配置类,但是Kotlin默认将它们定型为final。 有关 详情, 请参见 此处

  • 出于与上述相同的原因,必须声明@Bean函数为打开状态。

  • 函数上没有返回类型,因为Kotlin会推断这些类型。 这种类型推断是Kotlin我最喜欢的功能之一。

  • Kotlin对于 单表达式函数 具有隐式返回(且没有花括号) 当函数主体中只有一个表达式时,Kotlin会自动假定您要返回该值,因此不需要显式 return 或大括号。 对于具有多个表达式的主体, return 仍然是必需的,因为编译器可能无法猜测函数的返回类型。

  • new 初始化对象时 没有 关键字。 再加上类型推断,隐式返回和单条语句/不使用大括号,就构成了一个不错的紧凑型配置类。

在Kotlin中,Spring配置类对我来说是个好主意。 实际的代码差异只有4行代码(18对14),但是在Kotlin中,视觉噪声大大降低了。 对于我来说,必须将类和所有方法都声明为开放似乎有点笨拙,但由于类型推断,单表达式函数没有返回值以及这些类从Kotlin获得的其他改进,我愿意忽略它。

谢谢阅读。 在我们 在下一篇文章中 ,我们将看看使用科特林的数据类实现的POJO。

欢迎使用我们的Java 8-> Kotlin转换的第三部分,用于Spring Boot应用程序。 上次 我们看到将配置类转换为Kotlin如何帮助清除Java中所需的一些样板代码。

在第三部分中,我们将继续我们的主题“用Kotlin编写更少的代码”,并研究 Kotlin数据类 如何 帮助我们清理POJO数据类。

我们的 起点 是一个普通的旧Java对象(POJO),用于保存一些通过RabbitMQ发送的数据:

package com.example.email;

import java.io.Serializable;

public class EmailMessage implements Serializable {

    private final String toAddress;
    private final String subject;
    private final String body;

    public EmailMessage(String toAddress, String subject, String body) {
        this.toAddress = toAddress;
        this.subject = subject;
        this.body = body;
    }

    public String getToAddress() {
        return toAddress;
    }

    public String getSubject() {
        return subject;
    }

    public String getBody() {
        return body;
    }
}

下面是 同一类 的实现 科特林数据类

package com.example.email

import java.io.Serializable

data class EmailMessage(val toAddress: String, val subject: String, val body: String) : Serializable

该代码不仅比Java代码短得多,而且具有更多功能。 作为Kotlin数据类,此少量代码将获得:

  • 生成一 equals()/hashCode() 对的 实现

  • 默认 toString() 方法

  • 一种 copy() 允许轻松更改对象的各个属性的方法

  • 在赋值语句中 分解 对象 的能力

JSON反序列化 Spring Data JPA类 可以方便地使用此功能

当有人评估从Java到Kotlin的转换时,这些数据类是在Spring Boot应用程序中采用Kotlin的主要原因。 它们可以帮助您编写并维护更少的代码,而我想维护的代码也越少越好。

https://engineering.pivotal.io/post/spring-boot-application-with-kotlin/

原文  http://mp.weixin.qq.com/s?__biz=MzA5OTI2MTE3NA==&mid=2658338469&idx=5&sn=7f9f6f7c46aad32aac355394ae1b58cb
正文到此结束
Loading...