Spring Boot + Kotlin实现微服务 - piotrminkowski

19-01-19 banq
              

随着Kotlin语言的普及,它更常用于Spring Boot来构建后端服务。从版本5开始,Spring Framework引入了对Kotlin的一流支持。在本文中,我将向您展示使用Kotlin和Spring Boot 2进行微服务构建的示例。我将描述Spring Boot的一些有趣功能,在构建基于REST的后端微服务时,可以将其视为一组良好实践。

1.配置和依赖

要在Maven项目使用Kotlin你必须包括插件kotlin-maven-plugin,并且/src/main/kotlin,/src/test/kotlin目录生成配置。我们还将-Xjsr305编译器标志设置为strict。此选项负责检查对JSR-305注释的支持(例如@NotNull注释)。

<build>
    <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
    <plugins>
        <plugin>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <configuration>
                <args>
                    <arg>-Xjsr305=strict</arg>
                </args>
                <compilerPlugins>
                    <plugin>spring</plugin>
                </compilerPlugins>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>org.jetbrains.kotlin</groupId>
                    <artifactId>kotlin-maven-allopen</artifactId>
                    <version>${kotlin.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

我们还应该包括一些像kotlin-stdlib-jdk8和的核心Kotlin库kotlin-reflect。默认情况下,我们可以在start.spring.io上配置Kotlin项目,生成pom.xml。对于基于REST的应用程序,您还需要用于JSON序列化/反序列化的Jackson库。当然,我们必须包括用于Web应用程序的Spring启动器以及负责提供管理端点的Actuator。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib

我们使用最新的稳定版Spring Boot和Kotlin 1.2.71

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.2.RELEASE</version>
</parent>
<properties>
    <java.version>1.8</java.version>
    <kotlin.version>1.2.71</kotlin.version>
</properties>

2.架构应用

让我们从基础开始。如果您熟悉Spring Boot和Java,最大的区别在于主类声明。您将runApplication在Spring Boot应用程序类之外调用方法。主要类与Java中的相同,使用注释@SpringBootApplication。

@SpringBootApplication
class SampleSpringKotlinMicroserviceApplication
 
fun main(args: Array<String>) {
    runApplication<SampleSpringKotlinMicroserviceApplication>(*args)
}

我们的示例应用程序非常简单。它公开了一些REST端点,为模型对象提供CRUD操作。即使在这个代码控制器实现的代码片段中,您也可以看到一些不错的Kotlin功能。我们可以使用缩短的函数声明和推断的返回类型。注释@PathVariable不需要任何参数。输入参数名称被视为与变量名称相同。当然,我们使用与Java相同的注释。在Kotlin中,必须在构造函数中初始化声明为非null类型的每个属性。所以,如果你使用依赖注入初始化它,它必须声明为lateinit。这些在PersonController中实施:

@RestController
@RequestMapping("/persons")
class PersonController {
 
    @Autowired
    lateinit var repository: PersonRepository
 
    @GetMapping("/{id}")
    fun findById(@PathVariable id: Int): Person? = repository.findById(id)
 
    @GetMapping
    fun findAll(): List<Person> = repository.findAll()
 
    @PostMapping
    fun add(@RequestBody person: Person): Person = repository.save(person)
 
    @PutMapping
    fun update(@RequestBody person: Person): Person = repository.update(person)
 
    @DeleteMapping("/{id}")
    fun remove(@PathVariable id: Int): Boolean = repository.removeById(id)
 
}

Kotlin自动为声明为的类属性生成getter和setter var。此外,如果你声明模型作为数据类它产生equals,hashCode和toString方法。我们的模型类的声明Person非常简洁,如下所示。

data class Person(var id: Int?, var name: String, var age: Int, var gender: Gender)

我已经实现了自己的内存存储库类。我使用Kotlin扩展来操作元素列表。这个内置的Kotlin功能类似于Java流,不同之处在于您不必在Collection和之间执行任何转换Stream。

@Repository
class PersonRepository {
    val persons: MutableList<Person> = ArrayList()
 
    fun findById(id: Int): Person? {
        return persons.singleOrNull { it.id == id }
    }
 
    fun findAll(): List<Person> {
        return persons
    }
 
    fun save(person: Person): Person {
        person.id = (persons.maxBy { it.id!! }?.id ?: 0) + 1
        persons.add(person)
        return person
    }
 
    fun update(person: Person): Person {
        val index = persons.indexOfFirst { it.id == person.id }
        if (index >= 0) {
            persons[index] = person
        }
        return person
    }
 
    fun removeById(id: Int): Boolean {
        return persons.removeIf { it.id == id }
    }
 
}

3. 启用执行器端点

由于我们已将Spring Boot starter与Actuator一起包含在应用程序代码中,因此我们可以利用其生产就绪功能。Spring Boot Actuator为您提供了非常强大的工具来监控和管理您的应用程序。您可以提供高级健康检查,信息端点或将指标发送到众多监控系统,如InfluxDB。在包含Actuator工件之后,我们唯一要做的就是通过HTTP为我们的应用程序启用其所有端点。

management.endpoints.web.exposure.include: '*'

我们可以自定义Actuator端点,以提供有关我们应用的更多详细信息。一个好的做法是将有关版本和git提交的信息公开给info端点。像往常一样,Spring Boot为这些功能提供了自动配置,因此我们唯一需要做的就是包含一些Maven插件来构建配置pom.xml。build-info设定的目标是spring-boot-maven-plugin强制它生成包含有关版本的基本信息的属性文件。该文件位于目录中META-INF/build-info.properties。插件git-commit-id-plugin将git.properties在根目录中生成文件。

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>build-info</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>pl.project13.maven</groupId>
    <artifactId>git-commit-id-plugin</artifactId>
    <configuration>
        <failOnNoGitDirectory>false</failOnNoGitDirectory>
    </configuration>
</plugin>

现在,您应该使用mvn clean install命令构建应用程序,然后运行它。

$ java -jar target\sample-spring-kotlin-microservice-1.0-SNAPSHOT.jar

该info端点是在地址http://localhost:8080/actuator/info. 它为我们揭示了所有有趣的信息。

{
    "git":{
        "commit":{
            "time":"2019-01-14T16:20:31Z",
            "id":"f7cb437"
        },
        "branch":"master"
    },
    "build":{
        "version":"1.0-SNAPSHOT",
        "artifact":"sample-spring-kotlin-microservice",
        "name":"sample-spring-kotlin-microservice",
        "group":"pl.piomin.services",
        "time":"2019-01-15T09:18:48.836Z"
    }
}

4.启用API文档

构建信息和git属性可以很容易地注入到应用程序代码中。在某些情况下它可能很有用。其中一种情况是您已启用自动生成的API文档。最常用的工具是Swagger。您可以使用SpringFox Swagger项目轻松地将Swagger2与Spring Boot集成。首先,您需要包含以下依赖项pom.xml。

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

然后,您应该通过使用注释配置类来启用Swagger @EnableSwagger2。所需的信息是在BuildProperties和GitProperties,我们只需将它们注入到Swagger配置类中,如下所示。我们将它们设置为可选,以防止应用程序启动失败,以防它们不存在于类路径中。

@Configuration
@EnableSwagger2
class SwaggerConfig {
 
    @Autowired
    lateinit var build: Optional<BuildProperties>
    @Autowired
    lateinit var git: Optional<GitProperties>
 
    @Bean
    fun api(): Docket {
        var version = "1.0"
        if (build.isPresent && git.isPresent) {
            var buildInfo = build.get()
            var gitInfo = git.get()
            version = "${buildInfo.version}-${gitInfo.shortCommitId}-${gitInfo.branch}"
        }
        return Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo(version))
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths{ it.equals("/persons")}
                .build()
                .useDefaultResponseMessages(false)
                .forCodeGeneration(true)
    }
 
    @Bean
    fun uiConfig(): UiConfiguration {
        return UiConfiguration(java.lang.Boolean.TRUE, java.lang.Boolean.FALSE, 1, 1, ModelRendering.MODEL, java.lang.Boolean.FALSE, DocExpansion.LIST, java.lang.Boolean.FALSE, null, OperationsSorter.ALPHA, java.lang.Boolean.FALSE, TagsSorter.ALPHA, UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, null)
    }
 
    private fun apiInfo(version: String): ApiInfo {
        return ApiInfoBuilder()
                .title("API - Person Service")
                .description("Persons Management")
                .version(version)
                .build()
    }
 
}

文档在/swagger-ui.html路径下可用。除API文档外,还显示有关应用程序版本,git commit id和分支名称的完整信息。

5.选择您的应用服务器

Spring Boot Web可以在三个不同的嵌入式服务器上运行:Tomcat,Jetty或Undertow。默认情况下,它使用Tomcat。要更改默认服务器,您只需要包含合适的Spring Boot启动器并排除spring-boot-starter-tomcat。良好的做法可能是在应用程序构建期间启用服务器之间的切换。您可以通过声明Maven配置文件来实现它,如下所示。

<profiles>
    <profile>
        <id>tomcat</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>jetty</id>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-tomcat</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jetty</artifactId>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>undertow</id>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-tomcat</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-undertow</artifactId>
            </dependency>
        </dependencies>
    </profile>
</profiles>

现在,如果您想为应用程序启用除Tomcat之外的其他服务器,则应在Maven构建期间激活相应的配置文件。

$ mvn clean install -Pjetty

示例应用程序源代码在GitHub的存储库https://github.com/piomin/sample-spring-kotlin-microservice.git