Kotlin 协程 + Spring webflux 开发后端

前言

后端响应式是未来,吞吐量会更大,而资源占用更少,其用到了类似Android系统的Loop(事件循环)机制,而协程可以减少线程等待的消耗,并且同步式的编程方式使代码可读性更高,两个仿佛天生就是一对,所以就来简单的了解并配置一下Kotlin 协程 + Spring webflux的后端项目

正文

项目配置采用Gradle(毕竟我是做Android开发的,所以一切亲Android体系),数据库用的Mysql

build.gradle.kts

plugins {
    id("org.springframework.boot") version "2.3.5.RELEASE"
    id("io.spring.dependency-management") version "1.0.10.RELEASE"
    kotlin("jvm") version "1.4.10"
    kotlin("plugin.spring") version "1.4.10"
}

dependencies {
    //spring webflux 响应式服务端框架,使用方法基本等同spring mvc(除了返回值)
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    //webflux 返回值的协程支持
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
    //webflux await的协程支持
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive")
    //异步数据库支持
    implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
    //mysql 的异步数据库支持
    implementation("dev.miku:r2dbc-mysql")
    //json解析(默认就是jackson)
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    //webflux kt扩展
    implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
    //kt反射
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    //kotlin核心库
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    //网络请求
    implementation("com.squareup.okhttp3:okhttp:3.14.9")
}

...

目前项目结构是这样:

然后打开application.properties文件增加以下配置

#修改端口号
server.port=xxxxx (最好大于一万并小于65535,具体自行搜索)
#
#配置mysql r2dbc
spring.r2dbc.password=mysql密码
spring.r2dbc.username=mysql用户名
spring.r2dbc.url=r2dbcs:mysql://mysql的ip:mysql端口/mysql的数据库名

然后spring webflux的操作其他还是跟spring mvc是一样的,这里就不赘述,唯一不同的是返回值

示例controller

import kotlinx.coroutines.reactor.mono
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.r2dbc.core.DatabaseClient
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import javax.annotation.Resource

/**
 * creator: lt  2020/11/11  lt.dygzs@qq.com
 * effect : test的接口
 * warning:
 */
@RestController
@RequestMapping("/api/ad")
class TestInterface {

    @GetMapping("/findAll")
    fun findAll(): Flux<TestBean> //返回Flux<T>表示是个list

    @GetMapping("/addData")
    fun addData(): Mono<TestBean> //返回Mono<T>表示是单个对象
}

示例数据库表的实体类和数据库操作对象

import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import org.springframework.data.repository.kotlin.CoroutineSortingRepository

/**
 * creator: lt  2020/11/11  lt.dygzs@qq.com
 * effect : 表实体类
 * warning:
 */
@Table("feedback")
class TestBean(
        @Id
        var id: Long? = null,//如果id为null,则为自增,并且需要var
        val create_time: Long = 0,
        val content: String? = null,
        val user_id: Long = 0,
        val urls: String? = null,
        val is_solve: Int = 0,
        val solve_time: Long = 0,
)

/**
 * creator: lt  2020/11/11  lt.dygzs@qq.com
 * effect : 数据库操作类
 * warning:
 */
interface TestDB : CoroutineSortingRepository<TestBean, Long>

然后就可以在接口里去增删改查数据了,比如增加一条数据

或者直接声明挂起函数,如下(声明等同于上面的方法)

其中mono{}是开启了一个以Mono<T>为返回值的协程,而TestDB实现了CoroutineSortingRepository接口,所以其内的大部分方法都是suspend的
这里我们插入一条数据(挂起了),然后打印一下返回值(就是插入的对象),并返回这个对象

这个db提供了如savexxx,findxxx,deletexxxx等简单的操作,如果需要自己执行sql语句,需要如下:

需要注意的是,如果flux内的suspend lambda走完的话,这个接口就完成了,也就是说如果是在回调中在调用channel.send是无效的,需要改成使用协程的形式

自己执行sql语句还有以下的api:

//查询,返回Flux<T>
databaseClient.execute("select * from client_user").as(ClientUser.class)
                .fetch()
                .all();
//插入,返回Mono<Integer>
databaseClient.insert().into(ClientUser.class)
                .using(clientUser)
                .fetch().rowsUpdated();

 返回Flux也可以这样写

或者也可以这样自定义使用sql,参考: Redirecting...   1.8.2

interface TestDB : CoroutineSortingRepository<TestBean, Long> {
    @Query("select * from feedback")
    fun getAll(): Flux<TestBean>

    @Query("select * from feedback where id = :id")
    suspend fun get(id: Long): TestBean

    @Query("select * from feedback where id = 1")
    fun get2(): Mono<TestBean>

    suspend fun getById(id: Long): TestBean
}

api参考: Spring Data JPA - Reference Documentation  6.3 

然后使用基本就ok了,可以直接运行测试一下

扩展

1.可能你需要java和kt代码混写,但用的是gradle,可能会不去编译java代码,导致出现NoClassDefFoundError

此时只需要在项目的build.gradle.kts中的最外层添加如下代码即可,原因是java代码不知道代码入口了

sourceSets {
    main {
        java.srcDirs("src/main/kotlin")
    }
}

2.可能第一次自己写sql没有绑定到数据库表上,如下图,然后我就大概说一下怎么绑定,绑定完就会有sql的代码提示了

步骤如下:

然后如下简单配置一下

会弹出

在里面执行一下,然后在同步一下

use 你的数据库名 ;

然后回去

可以把默认的(所有文件)改成这个库,并把当前文件的私有设置删掉(或者你单独改某文件也行)

然后sql语句就有代码提示了

如果有什么写的不对的请大佬们指出,我用的还不熟练,相当于给自己写了一个文档 \嘿嘿

end

对Kotlin或KMP感兴趣的同学可以进Q群 101786950

如果这篇文章对您有帮助的话

可以扫码请我喝瓶饮料或咖啡(如果对什么比较感兴趣可以在备注里写出来)

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Kotlin + 协程 + Retrofit + MVVM 是一种非常优雅的方式来实现网络请求。下面是一个简单的示例: 首先,我们需要添加相关依赖库: ```gradle // Kotlin协程 implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' // Retrofit2 implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // ViewModel和LiveData implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0' ``` 接下来,我们创建一个数据类来表示网络请求返回的数据: ```kotlin data class Response<T>( val code: Int, val msg: String, val data: T? ) ``` 然后,我们创建一个接口来描述我们的API: ```kotlin interface ApiService { @GET("api/get_data") suspend fun getData(): Response<List<String>> } ``` 注意,我们在这里使用了 `suspend` 关键字来表示这个方法是一个挂起函数。 接下来,我们创建一个 `ApiService` 的实例: ```kotlin val retrofit = Retrofit.Builder() .baseUrl("https://example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() val api = retrofit.create(ApiService::class.java) ``` 现在,我们可以在我们的 ViewModel 中使用这个 `api` 对象来进行网络请求了: ```kotlin class MyViewModel : ViewModel() { private val _data = MutableLiveData<List<String>>() val data: LiveData<List<String>> = _data fun loadData() { viewModelScope.launch { val response = api.getData() if (response.code == 200) { _data.value = response.data } } } } ``` 在这里,我们使用了 `viewModelScope` 来启动一个协程来进行网络请求。如果请求成功,我们就将数据传递给 `_data` 变量,它是一个 LiveData 对象。 最后,我们可以在我们的 Activity 或 Fragment 中观察 `data` 变量来获取网络请求返回的数据: ```kotlin class MyActivity : AppCompatActivity() { private val viewModel by viewModels<MyViewModel>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_my) viewModel.data.observe(this, Observer { data -> // 在这里更新UI }) viewModel.loadData() } } ``` 这样,我们就使用 Kotlin + 协程 + Retrofit + MVVM 实现了一个优雅的网络请求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值