Springboot maven集成java+kotlin,支持lombok

1,526 阅读3分钟

概述

kotlin语言作为后起之秀拥有很多下一代语言的新特性,个人使用起来觉得相较于java,更安全、更简洁。但是项目组中的其他同事仍未接触过该语言,故而新项目并不能完全使用kotlin。同时,项目组也偏向于使用maven来做构建。
kotlin与java有互操作性,两者可以在项目中共存,本文主要介绍用maven集成两者的实现方式。

依赖

  • springboot 2.5.5
  • maven 3.8.5
  • kotlin 1.5.31
  • java 1.8

项目demo代码

gitee by peaksong
github by peaksong

重要插件

maven在打包构建有自己的一套生命周期,同时支持第三方以插件的方式,实现丰富的特性和功能。本次主要使用的插件分别是kotlin-maven-pluginspring-boot-maven-pluginmaven-compiler-plugin

  • kotlin-maven-plugin 主要用来处理kotlin代码
  • maven-compiler-plugin主要涉及对maven原有构建生命周期阶段和目标的修改
  • spring-boot-maven-plugin 主要用来构建一个可运行的jar包

在构建中会遇到各种问题,分别使用添加插件或修改插件配置的方式来解决。

kotlin-maven-plugin

下面简单地介绍各项配置的意义。

  • -Xjsr305=strict

尽管Java不支持在其类型系统中表示null安全,但是Spring Framework通过在org.springframework.lang包中声明的对工具友好的注释提供了整个Spring Framework API的空安全。默认情况下,在Kotlin中使用的Java API中的类型会被识别为放松了空检查的平台类型。 Kotlin对 JSR 305 annotations 的支持为Kotlin开发人员提供了整个Spring Framework API的null安全性,其优点是在编译时处理与null相关的问题。

  • all-open
    官网解析 https://kotlinlang.org/docs/all-open-plugin.html#maven 因为kotlin中所有的类默认都是不可继承,spring框架的代理就不好使了。所以kotlin代码里会满满的open。这个插件可以省掉这些open,为指定注解注解的类在编译时添加open。
    如果使用了jpa, 一般会添加如下的option
<option>all-open:annotation=javax.persistence.Entity</option>
<option>all-open:annotation=javax.persistence.Embeddable</option>
<option>all-open:annotation=javax.persistence.MappedSuperclass</option>

如果代码中还有自定义的注解用于大多数类,也可以一并加入该option。

  • spring 在all-open的官网解析中也能看到,spring插件依赖于all-open。其功能是指定一些spring常用注解来添加open。
    该插件指定了以下注解: @Component@Async@Transactional@Cacheable 以及 @SpringBootTest
    同时,由于元注解的支持,标注有 @Configuration@Controller@RestController@Service 或者 @Repository 的类会自动打开,因为这些注解标注有元注解 @Component

  • no-arg
    官网解析 https://kotlinlang.org/docs/no-arg-plugin.html#maven
    与all-open类似,主要是用来添加无参构造方法,这在jpa中使用data class定义entity的场景中十分有效。

  • jpa 与 kotlin-spring 插件类似,kotlin-jpa 是在 no-arg 之上的一层包装。该插件自动指定了 @Entity@Embeddable@MappedSuperclass 这几个 无参 注解。

  • lombok
    官网解析 https://kotlinlang.org/docs/lombok.html
    添加对lombok的支持,比如在kotlin使用lombok注解的类。

本文的实现没有使用kapt。读者可以仿照以下项目,自行选择合适的解决方案。 github.com/kotlin-hand…

同时,不忘提醒一句,要检查插件是否安装成功,只要安装成功就一定不会有问题。笔者在实践时,阿里云仓库没有同步到中央仓库1.5.31版本的插件,导致了一些反复。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.peaksong</groupId>
    <artifactId>boot-kotlin-maven</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>boot-kotlin-maven</name>
    <description>boot-kotlin-maven</description>
    <properties>
        <java.version>1.8</java.version>
        <kotlin.version>1.5.31</kotlin.version>
        <springboot.verion>2.5.5</springboot.verion>

        <lombok.version>1.18.20</lombok.version>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.sourceEncoding>UTF-8</project.reporting.sourceEncoding>

        <maven.compile.source>1.8</maven.compile.source>
        <maven.compile.target>1.8</maven.compile.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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-jdk8</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                
                <configuration>
                    <args>
                        <arg>-Xjsr305=strict</arg>
                    </args>
                    <compilerPlugins>
                        <plugin>spring</plugin>
                        <plugin>lombok</plugin>
<!--                        <plugin>jpa</plugin>-->
                        <plugin>all-open</plugin>
                        <plugin>no-arg</plugin>
                    </compilerPlugins>

                    <pluginOptions>
<!--                        <option>all-open:annotation=javax.persistence.Entity</option>-->
<!--                        <option>all-open:annotation=javax.persistence.Embeddable</option>-->
<!--                        <option>all-open:annotation=javax.persistence.MappedSuperclass</option>-->
                        <!--                        <pluginOption>no-arg:annotation=</pluginOption>-->
                        <!--                        <pluginOption>all-open:annotation=</pluginOption>-->
                    </pluginOptions>
                </configuration>

                <dependencies>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-allopen</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-noarg</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>

                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-lombok</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>

                </dependencies>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <sourceDirs>
                                <sourceDir>src/main/java</sourceDir>
                                <sourceDir>src/main/kotlin</sourceDir>
                            </sourceDirs>
                        </configuration>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                        <configuration>
                            <sourceDirs>
                                <sourceDir>src/test/java</sourceDir>
                                <sourceDir>src/test/kotlin</sourceDir>
                            </sourceDirs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>



            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>${maven.compile.source}</source>
                    <target>${maven.compile.target}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
<!--                    <annotationProcessorPaths>-->
<!--                        <path>-->
<!--                            <groupId>org.projectlombok</groupId>-->
<!--                            <artifactId>lombok</artifactId>-->
<!--                            <veersion>${lombok.version}</veersion>-->
<!--                        </path>-->
<!--                    </annotationProcessorPaths>-->
<!--                    <compilerArgs>-->
<!--                        <arg>-parameters</arg>-->
<!--                    </compilerArgs>-->
                </configuration>
                <executions>
                    <!-- 替换 default-compile, 因为它会被 maven 特别处理 -->
                    <!-- 去除会导致java中引用kotlin代码报错 找不到包或者符号 -->
                    <execution>
                        <id>default-compile</id>
                        <phase>none</phase>
                    </execution>
                    <!-- 替换 default-testCompile, 因为它会被 maven 特别处理 -->
                    <execution>
                        <id>default-testCompile</id>
                        <phase>none</phase>
                    </execution>
                    <execution>
                        <id>java-compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>java-test-compile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${springboot.verion}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

</project>