2.3 Web RESTFul HelloWorld

本节介绍使用 Kotlin 结合 SpringBoot 开发一个RESTFul版本的 Hello.World

  1. 新建gradle,kotlin工程:

打开IDEA的File > New > Project , 如下图

Kotlin极简教程

按照界面操作,输入相应的工程名等信息,即可新建一个使用Gradle构建的标准Kotlin工程。

  1. build.gradle 基本配置

IDEA自动生成的Gradle配置文件如下:

  1. group 'com.easy.kotlin'
  2. version '1.0-SNAPSHOT'
  3. buildscript {
  4. ext.kotlin_version = '1.1.2-2'
  5. repositories {
  6. mavenCentral()
  7. }
  8. dependencies {
  9. // Kotlin Gradle插件
  10. classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  11. }
  12. }
  13. apply plugin: 'java'
  14. apply plugin: 'kotlin'
  15. sourceCompatibility = 1.8
  16. targetCompatibility = 1.8
  17. repositories {
  18. mavenCentral()
  19. }
  20. dependencies {
  21. compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
  22. testCompile group: 'junit', name: 'junit', version: '4.12'
  23. }

从上面的配置文件我们可以看出,IDEA已经自动把Gradle 构建Kotlin工程插件 kotlin-gradle-plugin,以及Kotlin标准库kotlin-stdlib添加到配置文件中了。

  1. 配置SpringBoot相关内容

下面我们来配置SpringBoot相关内容。首先在构建脚本里面添加ext变量springBootVersion。

  1. ext.kotlin_version = '1.1.2-2'
  2. ext.springboot_version = '1.5.2.RELEASE'

然后在构建依赖里添加spring-boot-gradle-plugin

  1. buildscript {
  2. ...
  3. dependencies {
  4. // Kotlin Gradle插件
  5. classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  6. // SpringBoot Gradle插件
  7. classpath("org.springframework.boot:spring-boot-gradle-plugin:$springboot_version")
  8. // Kotlin整合SpringBoot的默认无参构造函数,默认把所有的类设置open类插件
  9. classpath("org.jetbrains.kotlin:kotlin-noarg:$kotlin_version")
  10. classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlin_version")
  11. }
  12. }
  1. 配置无参(no-arg)、全开放(allopen)插件

其中,org.jetbrains.kotlin:kotlin-noarg是无参(no-arg)编译器插件,它为具有特定注解的类生成一个额外的零参数构造函数。 这个生成的构造函数是合成的,因此不能从 Java 或 Kotlin 中直接调用,但可以使用反射调用。 这样我们就可以使用 Java Persistence API(JPA)实例化 data 类。

其中,org.jetbrains.kotlin:kotlin-allopen 是全开放编译器插件。我们使用Kotlin 调用Java的Spring AOP框架和库,需要类为 open(可被继承实现),而Kotlin 类和函数都是默认 final 的,这样我们需要为每个类和函数前面加上open修饰符。

这样的代码写起来,可费事了。还好,我们有all-open 编译器插件。它会适配 Kotlin 以满足这些框架的需求,并使用指定的注解标注类而其成员无需显式使用 open 关键字打开。 例如,当我们使用 Spring 时,就不需要打开所有的类,跟我们在Java中写代码一样,只需要用相应的注解标注即可,如 @Configuration@Service

完整的build.gradle配置文件如下

  1. group 'com.easy.kotlin'
  2. version '1.0-SNAPSHOT'
  3. buildscript {
  4. ext.kotlin_version = '1.1.2-2'
  5. ext.springboot_version = '1.5.2.RELEASE'
  6. repositories {
  7. mavenCentral()
  8. }
  9. dependencies {
  10. // Kotlin Gradle插件
  11. classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  12. // SpringBoot Gradle插件
  13. classpath("org.springframework.boot:spring-boot-gradle-plugin:$springboot_version")
  14. // Kotlin整合SpringBoot的默认无参构造函数,默认把所有的类设置open类插件
  15. // 无参(no-arg)编译器插件为具有特定注解的类生成一个额外的零参数构造函数。 这个生成的构造函数是合成的,因此不能从 Java 或 Kotlin 中直接调用,但可以使用反射调用。 这允许 Java Persistence API(JPA)实例化 data 类,虽然它从 Kotlin 或 Java 的角度看没有无参构造函数
  16. classpath("org.jetbrains.kotlin:kotlin-noarg:$kotlin_version")
  17. // 全开放插件(kotlin-allopen)
  18. classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlin_version")
  19. }
  20. }
  21. apply plugin: 'java'
  22. apply plugin: 'kotlin'
  23. //Kotlin整合SpringBoot需要的spring,jpa,org.springframework.boot插件
  24. //Kotlin-spring 编译器插件,它根据 Spring 的要求自动配置全开放插件。
  25. apply plugin: 'kotlin-spring'
  26. //该插件指定 @Entity 和 @Embeddable 注解作为应该为一个类生成无参构造函数的标记。
  27. apply plugin: 'kotlin-jpa'
  28. apply plugin: 'org.springframework.boot'
  29. sourceCompatibility = 1.8
  30. targetCompatibility = 1.8
  31. repositories {
  32. mavenCentral()
  33. }
  34. dependencies {
  35. compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
  36. testCompile group: 'junit', name: 'junit', version: '4.12'
  37. compile("org.springframework.boot:spring-boot-starter-web")
  38. testCompile("org.springframework.boot:spring-boot-starter-test")
  39. compile("org.springframework.boot:spring-boot-starter-data-jpa")
  40. compile('mysql:mysql-connector-java:5.1.13')
  41. }
  1. 配置application.properties
  1. spring.datasource.url = jdbc:mysql://localhost:3306/easykotlin
  2. spring.datasource.username = root
  3. spring.datasource.password = root#spring.datasource.driverClassName = com.mysql.jdbc.Driver# Specify the DBMS
  4. spring.jpa.database = MYSQL# Keep the connection alive if idle for a long time (needed in production)
  5. spring.datasource.testWhileIdle = true
  6. spring.datasource.validationQuery = SELECT 1# Show or not log for each sql query
  7. spring.jpa.show-sql = true# Hibernate ddl auto (create, create-drop, update)
  8. spring.jpa.hibernate.ddl-auto = update# Naming strategy
  9. spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy# The SQL dialect makes Hibernate generate better SQL for the chosen database
  10. spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
  11. server.port=8000
  1. 整体工程架构

UNIX操作系统说,“一切都是文件”。所以,我们 的所有的源代码、字节码、工程资源文件等等,一切都是文件。文件里面存的是字符串(01也当做是字符)。各种框架、库、编译器,解释器,都是对这些字符串流进行过滤,最后映射成01机器码(或者CPU微指令码等),最终落地到硬件上的高低电平。

整体工程目录如下:

  1. .
  2. ├── README.md
  3. ├── build
  4. └── kotlin-build
  5. └── caches
  6. └── version.txt
  7. ├── build.gradle
  8. ├── easykotlin.sql
  9. ├── settings.gradle
  10. └── src
  11. ├── main
  12. ├── java
  13. ├── kotlin
  14. └── com
  15. └── easy
  16. └── kotlin
  17. ├── Application.kt
  18. ├── controller
  19. ├── HelloWorldController.kt
  20. └── PeopleController.kt
  21. ├── entity
  22. └── People.kt
  23. ├── repository
  24. └── PeopleRepository.kt
  25. └── service
  26. └── PeopleService.kt
  27. └── resources
  28. ├── application.properties
  29. └── banner.txt
  30. └── test
  31. ├── java
  32. ├── kotlin
  33. └── resources
  34. 19 directories, 13 files

一切尽在不言中,静静地看工程文件结构。

直接写个HelloWorldController

  1. package com.easy.kotlin.controller
  2. import org.springframework.web.bind.annotation.GetMapping
  3. import org.springframework.web.bind.annotation.RestController
  4. @RestController
  5. class HelloWorldController {
  6. @GetMapping(value = *arrayOf("/helloworld", "/"))
  7. fun helloworld(): Any {
  8. return "Hello,World!"
  9. }
  10. }

我们再写个访问数据库的标准四层代码

写领域模型类People

  1. package com.easy.kotlin.entity
  2. import java.util.*
  3. import javax.persistence.Entity
  4. import javax.persistence.GeneratedValue
  5. import javax.persistence.GenerationType
  6. import javax.persistence.Id
  7. /**
  8. * Created by jack on 2017/6/6.
  9. */
  10. @Entity
  11. class People(
  12. @Id @GeneratedValue(strategy = GenerationType.AUTO)
  13. val id: Long?,
  14. val firstName: String?,
  15. val lastName: String?,
  16. val gender: String?,
  17. val age: Int?,
  18. val gmtCreated: Date,
  19. val gmtModified: Date
  20. ) {
  21. override fun toString(): String {
  22. return "People(id=$id, firstName='$firstName', lastName='$lastName', gender='$gender', age=$age, gmtCreated=$gmtCreated, gmtModified=$gmtModified)"
  23. }
  24. }

写PeopleRepository

  1. package com.easy.kotlin.repository
  2. import com.easy.kotlin.entity.People
  3. import org.springframework.data.repository.CrudRepository
  4. interface PeopleRepository : CrudRepository<People, Long> {
  5. fun findByLastName(lastName: String): List<People>?
  6. }

写PeopleService

  1. package com.easy.kotlin.service
  2. import com.easy.kotlin.entity.People
  3. import com.easy.kotlin.repository.PeopleRepository
  4. import org.springframework.beans.factory.annotation.Autowired
  5. import org.springframework.stereotype.Service
  6. @Service
  7. class PeopleService : PeopleRepository {
  8. @Autowired
  9. val peopleRepository: PeopleRepository? = null
  10. override fun findByLastName(lastName: String): List<People>? {
  11. return peopleRepository?.findByLastName(lastName)
  12. }
  13. override fun <S : People?> save(entity: S): S? {
  14. return peopleRepository?.save(entity)
  15. }
  16. override fun <S : People?> save(entities: MutableIterable<S>?): MutableIterable<S>? {
  17. return peopleRepository?.save(entities)
  18. }
  19. override fun delete(entities: MutableIterable<People>?) {
  20. }
  21. override fun delete(entity: People?) {
  22. }
  23. override fun delete(id: Long?) {
  24. }
  25. override fun findAll(ids: MutableIterable<Long>?): MutableIterable<People>? {
  26. return peopleRepository?.findAll(ids)
  27. }
  28. override fun findAll(): MutableIterable<People>? {
  29. return peopleRepository?.findAll()
  30. }
  31. override fun exists(id: Long?): Boolean {
  32. return peopleRepository?.exists(id)!!
  33. }
  34. override fun count(): Long {
  35. return peopleRepository?.count()!!
  36. }
  37. override fun findOne(id: Long?): People? {
  38. return peopleRepository?.findOne(id)
  39. }
  40. override fun deleteAll() {
  41. }
  42. }

写PeopleController

  1. package com.easy.kotlin.controller
  2. import com.easy.kotlin.service.PeopleService
  3. import org.springframework.beans.factory.annotation.Autowired
  4. import org.springframework.stereotype.Controller
  5. import org.springframework.web.bind.annotation.GetMapping
  6. import org.springframework.web.bind.annotation.RequestParam
  7. import org.springframework.web.bind.annotation.ResponseBody
  8. @Controller
  9. class PeopleController {
  10. @Autowired
  11. val peopleService: PeopleService? = null
  12. @GetMapping(value = "/hello")
  13. @ResponseBody
  14. fun hello(@RequestParam(value = "lastName") lastName: String): Any {
  15. val peoples = peopleService?.findByLastName(lastName)
  16. val map = HashMap<Any, Any>()
  17. map.put("hello", peoples!!)
  18. return map
  19. }
  20. }
  1. 运行测试

点击Gradle的bootRun , 如下图

Kotlin极简教程

如果没有异常,启动成功,我们将看到以下输出:

Kotlin极简教程

打开浏览器,访问请求:

http://127.0.0.1:8000/

输出响应:

  1. Hello,World!

访问

http://127.0.0.1:8000/hello?lastName=chen

  1. // 20170607115700
  2. // http://127.0.0.1:8000/hello?lastName=chen
  3. {
  4. "hello": [
  5. {
  6. "id": 1,
  7. "firstName": "Jason",
  8. "lastName": "Chen",
  9. "gender": "Male",
  10. "age": 28,
  11. "gmtCreated": 1496768497000,
  12. "gmtModified": 1496768497000
  13. },
  14. {
  15. "id": 3,
  16. "firstName": "Corey",
  17. "lastName": "Chen",
  18. "gender": "Female",
  19. "age": 20,
  20. "gmtCreated": 1496768497000,
  21. "gmtModified": 1496768497000
  22. }
  23. ...
  24. ]
  25. }

本节示例工程源代码:

https://github.com/EasyKotlin/easy_kotlin_chapter2_hello_world_springboot_restful