本次 More Time 为观众朋友带来拿 Kotlin 写 Spring的介绍。
Spring 官方支持 Kotlin,看这篇报道 , 17年就支持了,17之前可能会有一些问题。现在19年已经完全可用了。
其实也有个 Spring Scala 项目。不过这个项目不是官方支持的。
因此,Spring 官方支持的语言就是
- Java
- Groovy
- Kotlin
好,接下来看怎么用。
安装
- 新项目
直接在 Spring Initializar 里装一个就知道了。页面上或者 IDEA 上都有。
- 和Java混写
请点击阅读原文看附录,用这份 pom.xml 绝对没错,时间过长,我也不记得从哪里抄来的了😈
接下来看几个关键点的用法
@Autowired
Spring的核心就是 IOC和AOP,大家都知道。不会AOP可以,不会IOC就说不过去了。
当然了,这里用IOC这个词也是和AOP对仗押韵,通常我会称之为依赖注入,DI。
@Autowired
用来注入 stereostype (controller, Component, Service这些的) 和 @Bean
修饰的对象,即 Spring Bean。
有3种方式
- 字段注入
- 构造函数注入
- setter 注入。
字段注入
@Component
class A { }
@Component
class B {
@Autowired private A a;
}
构造器注入
@Component
class B {
A a;
B(A a) {
this.a = a;
}
}
setter 注入
@Component
class B {
A a;
@Autowired
void setA(A a) {
this.a = a;
}
}
Java里面显然是字段注入看得最爽,当然爽是有代价的,就是单测的时候GG。
就是如果没有Spring环境,就不能实例化一个能用的对象。依赖的东西动无法装载进去。
有一种观点就是单测的时候别加载Spring环境,加载Spring环境的是集成测试。
setter注入代码量比构造器注入还多,不考虑。
并且如果这种情况 final A a;
那么就只能构造器注入了。对于Java来说多一个 final
看上去噪音多了,实际用处也不大。没事把 @Autowired
的字段给改掉的人还是太少了。
OK,看Kotlin怎么做的
@Component
class A {}
@Component
class B(val a: A) {}
啥 this.a = a
不需要。
这就是 kotlin 的 主构造器语法 。
Java里构造器注入看上去就头大,而 Kotlin 构造器注入非常优雅。
lateint 注入
在JUnit4 里面测试类只能是空的构造函数,JUnit5不用,所以JUnit4只能这么写
@SpringBootTest
class DemoApplicationTests {
@Autowired
lateinit var a: A
@Test
fun contextLoads() {
assertThat(a).isNotNull
}
}
如此注入部分就讲完了。
@Value
Kotlin 支持字符串插值这一神器。
val a = 1
val b = "a"
println("$a$b")
输出 1a
但是 Spring 里面读 .properties 的也是用 $
啊。
所以 Kotlin 里面用 \$
表示 $
即
@Component
class A(@Value("\${x}") val x: String) {}
@Controller
kotlin 支持 =
的函数写法,所以一个可行的时间是吧 controller 做成很薄的一层,完全交给 service 层。更不要再 controller 写 返回 ResponseEntity.badRequest()
的逻辑。统一交给异常处理类。
即这种结构
@Service
class MyService {
fun f() { }
}
@RestController
class IndexController(val myService: MyService) {
@GetMapping("/")
fun index() = myService.f()
}
@ControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(Exception::class)
fun not1Error(e: Exception): ResponseEntity<String> =
ResponseEntity.badRequest().body(e.message)
}
BTW
- gradle 支持 Kotlin 代替 groovy了,但我用 Maven。
- 有些注解的参数可能和 Java不一样,一般搞个
arrayOf
就好了
推荐阅读
- https://spring.io/blog/2016/02/15/developing-spring-boot-applications-with-kotlin
- https://spring.io/guides/tutorials/spring-boot-kotlin/
附录
<project>
<properties>
<kotlin.version>1.3.50</kotlin.version>
</properties>
<dependencies>
<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>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals> <goal>compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<goals> <goal>test-compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/test/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<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>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<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>
</project>