图片

图片


本文讲解 Spring Boot2 基础下,如何使用 Kotlin,并无缝整合与完美交融。为了让读者更加熟悉 Kotlin 的语法糖,笔者会在未来的几篇文章中,聊聊 Kotlin 的新特性及其语法糖。


环境依赖

修改 POM 文件,添加 spring boot 依赖。

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>2.0.2.RELEASE</version>    <relativePath/></parent><dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-jdbc</artifactId>    </dependency></dependencies>

紧接着,我们需要添加 mysql 依赖。

<dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>5.1.35</version></dependency><dependency>    <groupId>com.alibaba</groupId>    <artifactId>druid</artifactId>    <version>1.0.14</version></dependency>

最后,添加 Kotlin 依赖。

<dependency>    <groupId>org.jetbrains.kotlin</groupId>    <artifactId>kotlin-stdlib-jdk8</artifactId></dependency><dependency>    <groupId>org.jetbrains.kotlin</groupId>    <artifactId>kotlin-reflect</artifactId></dependency><dependency>    <groupId>org.jetbrains.kotlin</groupId>    <artifactId>kotlin-stdlib</artifactId></dependency>

注意的是,在 Kotlin 中,data class 默认没有无参构造方法,并且 data class 默认为 final 类型,不可以被继承。注意的是,如果我们使用 Spring + Kotlin 的模式,那么使用 @autowared 就可能遇到这个问题。因此,我们可以添加 NoArg 为标注的类生成无参构造方法。使用 AllOpen 为被标注的类去掉 final,允许被继承。

<plugin>    <artifactId>kotlin-maven-plugin</artifactId>    <groupId>org.jetbrains.kotlin</groupId>    <version>${kotlin.version}</version>    <executions>        <execution>            <id>compile</id>            <goals> <goal>compile</goal> </goals>        </execution>        <execution>            <id>test-compile</id>            <goals> <goal>test-compile</goal> </goals>        </execution>    </executions>    <dependencies>        <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-allopen</artifactId>            <version>${kotlin.version}</version>        </dependency>    </dependencies></plugin>

至此,我们 Maven 的依赖环境大致配置完毕。完整的源码,可以参见文末 GitHub 仓库。

数据源

方案一 使用 Spring Boot 默认配置

使用 Spring Boot 默认配置,不需要在创建 dataSource 和 jdbcTemplate 的 Bean。

src/main/resources/application.properties 中配置数据源信息。

spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3307/springboot_dbspring.datasource.username=rootspring.datasource.password=root

方案二 手动创建

src/main/resources/config/source.properties 中配置数据源信息。

# mysqlsource.driverClassName = com.mysql.jdbc.Driversource.url = jdbc:mysql://localhost:3306/springboot_dbsource.username = rootsource.password = root

这里, 创建 dataSource 和jdbcTemplate。

@Configuration@EnableTransactionManagement@PropertySource(value = *arrayOf("classpath:config/source.properties"))open class BeanConfig {    @Autowired    private lateinit var env: Environment    @Bean    open fun dataSource(): DataSource {        val dataSource = DruidDataSource()        dataSource.driverClassName = env!!.getProperty("source.driverClassName").trim()        dataSource.url = env.getProperty("source.url").trim()        dataSource.username = env.getProperty("source.username").trim()        dataSource.password = env.getProperty("source.password").trim()        return dataSource    }    @Bean    open fun jdbcTemplate(): JdbcTemplate {        val jdbcTemplate = JdbcTemplate()        jdbcTemplate.dataSource = dataSource()        return jdbcTemplate    }}

脚本初始化

先初始化需要用到的 SQL 脚本。

CREATE DATABASE /*!32312 IF NOT EXISTS*/`springboot_db` /*!40100 DEFAULT CHARACTER SET utf8 */;USE `springboot_db`;DROP TABLE IF EXISTS `t_author`;CREATE TABLE `t_author` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',  `real_name` varchar(32) NOT NULL COMMENT '用户名称',  `nick_name` varchar(32) NOT NULL COMMENT '用户匿名',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

使用 JdbcTemplate 操作

实体对象

class Author {    var id: Long? = null    var realName: String? = null    var nickName: String? = null}

DAO相关

interface AuthorDao {    fun add(author: Author): Int    fun update(author: Author): Int    fun delete(id: Long): Int    fun findAuthor(id: Long): Author?    fun findAuthorList(): List<Author>}

我们来定义实现类,通过 JdbcTemplate 定义的数据访问操作。

@Repositoryopen class AuthorDaoImpl : AuthorDao {    @Autowired    private lateinit var jdbcTemplate: JdbcTemplate    override fun add(author: Author): Int {        return jdbcTemplate.update("insert into t_author(real_name, nick_name) values(?, ?)",                author.realName, author.nickName)    }    override fun update(author: Author): Int {        return jdbcTemplate.update("update t_author set real_name = ?, nick_name = ? where id = ?",                *arrayOf(author.realName, author.nickName, author.id))    }    override fun delete(id: Long): Int {        return jdbcTemplate.update("delete from t_author where id = ?", id)    }    override fun findAuthor(id: Long): Author? {        val list = jdbcTemplate.query<Author>("select * from t_author where id = ?",                arrayOf<Any>(id), BeanPropertyRowMapper(Author::class.java))        return list?.get(0);    }    override fun findAuthorList(): List<Author> {        return jdbcTemplate.query("select * from t_author", arrayOf(), BeanPropertyRowMapper(Author::class.java))    }}

Service相关

interface AuthorService {    fun add(author: Author): Int    fun update(author: Author): Int    fun delete(id: Long): Int    fun findAuthor(id: Long): Author?    fun findAuthorList(): List<Author>}

我们来定义实现类,Service 层调用 Dao 层的方法,这个是典型的套路。

@Service("authorService")open class AuthorServiceImpl : AuthorService {    @Autowired    private lateinit var authorDao: AuthorDao    override fun update(author: Author): Int {        return this.authorDao.update(author)    }    override fun add(author: Author): Int {        return this.authorDao.add(author)    }    override fun delete(id: Long): Int {        return this.authorDao.delete(id)    }    override fun findAuthor(id: Long): Author? {        return this.authorDao.findAuthor(id)    }    override fun findAuthorList(): List<Author> {        return this.authorDao.findAuthorList()    }}

Controller相关

为了展现效果,我们先定义一组简单的 RESTful API 接口进行测试。

@RestController@RequestMapping(value = "/authors")class AuthorController {    @Autowired    private lateinit var authorService: AuthorService    /**     * 查询用户列表     */    @RequestMapping(method = [RequestMethod.GET])    fun getAuthorList(request: HttpServletRequest): Map<String, Any> {        val authorList = this.authorService.findAuthorList()        val param = HashMap<String, Any>()        param["total"] = authorList.size        param["rows"] = authorList        return param    }    /**     * 查询用户信息     */    @RequestMapping(value = "/{userId:\\d+}", method = [RequestMethod.GET])    fun getAuthor(@PathVariable userId: Long, request: HttpServletRequest): Author {        return authorService.findAuthor(userId) ?: throw RuntimeException("查询错误")    }    /**     * 新增方法     */    @RequestMapping(method = [RequestMethod.POST])    fun add(@RequestBody jsonObject: JSONObject) {        val userId = jsonObject.getString("user_id")        val realName = jsonObject.getString("real_name")        val nickName = jsonObject.getString("nick_name")        val author = Author()        author.id = java.lang.Long.valueOf(userId)        author.realName = realName        author.nickName = nickName        try {            this.authorService.add(author)        } catch (e: Exception) {            throw RuntimeException("新增错误")        }    }    /**     * 更新方法     */    @RequestMapping(value = "/{userId:\\d+}", method = [RequestMethod.PUT])    fun update(@PathVariable userId: Long, @RequestBody jsonObject: JSONObject) {        var author = this.authorService.findAuthor(userId)        val realName = jsonObject.getString("real_name")        val nickName = jsonObject.getString("nick_name")        try {            if (author != null) {                author.realName = realName                author.nickName = nickName                this.authorService.update(author)            }        } catch (e: Exception) {            throw RuntimeException("更新错误")        }    }    /**     * 删除方法     */    @RequestMapping(value = "/{userId:\\d+}", method = [RequestMethod.DELETE])    fun delete(@PathVariable userId: Long) {        try {            this.authorService.delete(userId)        } catch (e: Exception) {            throw RuntimeException("删除错误")        }    }}

最后,我们通过 SpringKotlinApplication 运行程序。

@SpringBootApplication(scanBasePackages = ["com.lianggzone.demo.kotlin"])open class SpringKotlinApplication{    fun main(args: Array<String>) {        SpringApplication.run(SpringKotlinApplication::class.java, *args)    }}

关于测试

这里,笔者推荐 IDEA 的 Editor REST Client。IDEA 的 Editor REST Client 在 IntelliJ IDEA 2017.3 版本就开始支持,在 2018.1 版本添加了很多的特性。事实上,它是 IntelliJ IDEA 的 HTTP Client 插件。参见笔者之前的另一篇文章: 快速测试 API 接口的新技能

### 查询用户列表GET http://localhost:8080/authorsAccept : application/jsonContent-Type : application/json;charset=UTF-8### 查询用户信息GET http://localhost:8080/authors/15Accept : application/jsonContent-Type : application/json;charset=UTF-8### 新增方法POST http://localhost:8080/authorsContent-Type: application/json{    "user_id": "21",    "real_name": "梁桂钊",    "nick_name": "梁桂钊"}### 更新方法PUT http://localhost:8080/authors/21Content-Type: application/json{    "real_name" : "lianggzone",    "nick_name": "lianggzone"}### 删除方法DELETE http://localhost:8080/authors/21Accept : application/jsonContent-Type : application/json;charset=UTF-8

总结

通过,上面这个简单的案例,我们发现 Spring Boot 整合 Kotlin 非常容易,并简化 Spring 应用的初始搭建以及开发过程。为了让读者更加熟悉 Kotlin 的语法糖,笔者会在未来的几篇文章中,聊聊 Kotlin 的新特性及其语法糖。


更多相关文章

  1. 探寻泛型方法ClassCastException元凶
  2. 如何使用java语言求一个正整数的平方根?(自定义Sqrt方法)
  3. 设计模式之模板方法模式
  4. Java实现定时任务的三种方法
  5. 使用 ThreadLocal 变量的时机和方法
  6. clone 方法是如何工作的
  7. VS中scanf等函数报错解决方法
  8. jQuery编程基础精华02(属性、表单过滤器,元素的each,表单选择器,子元
  9. jQuery的$.getJSON方法在IE浏览器下失效的解决方案

随机推荐

  1. MyBatis之Mapper XML 文件详解(六)-缓存
  2. MyBatis之Mapper XML 文件详解(四)-JDBC
  3. 计算机网络面试知识点(1)基本概述
  4. SpringBoot整合Netty搭建高性能Websocket
  5. java并发编程CAS机制原理分析(面试必问,学
  6. Android(安卓)Service、Messenger进程间
  7. JDBC原理分析(包括基本的使用方式和面试题
  8. Spring Ioc 实例化 Bean 对象有几种方式?
  9. Springboot实现定时任务
  10. 纳税服务系统总结