Kodein在ktor中的使用

Kodein
然后添加依赖项:
• 使用Gradle 6+
dependencies {
implementation ‘org.kodein.di:kodein-di:7.4.0’
}
从6.3.0在JVM上,你必须把目标定JDK 1.8最低!
compileKotlin {
kotlinOptions.jvmTarget = “1.8”
}

在Kodein-DI中,绑定是在DI块中声明的。语法非常简单:
示例:DI容器的初始化

val kodein = DI {
    bind<Random>() with provider { SecureRandom() }
    bind<Database>() with singleton { SQLiteDatabase() }
}

Kodein-DI提供可管理的实现许多绑定:provider,singleton,factory,multiton,instance,和更多
• Provider binding : 每次加载都生成新的实例,无参, 传入() -> T
• Singleton binding : 单例 传入 () → T
• Eager singleton : 单例 创建了 Kodein 实例后立即实例化 传入 () -> T
• Factory binding : 每次加载都生成新的实例,需要参数,传入(A) -> T
• Multiton binding : 有参的单例,同样参数同样实例,传入 (A) -> T
• Tagged bindings : 通过 tag 来区分同类型不同的实例
,Kodein-DI允许您tag绑定:添加其他信息以对其进行标记。
在使用中如果使用了tag则在调用时候也要带上tag
示例:标记的绑定

val kodein = DI {
    bind<Database>(tag = "local") with singleton { SQLiteDatabase() }
    bind<Database>(tag = "remote") with provider { DatabaseProxy() }
}

分离
Kodein-DI允许您定义绑定模块,以后可以将其导入DI容器中:
示例:DI模块

val module = DI.Module {
    bind<Database>(tag = "local") with singleton { SQLiteDatabase() }
    bind<Database>(tag = "remote") with provider { DatabaseProxy() }
}

示例:导入DI模块

val di = DI {
    import(module)
}

注入与检索

Kodein-DI支持两种不同的方法来允许业务单位访问其依赖关系:注入和检索。
• 注入依赖项时,将在构造时为该类提供其依赖项。
• 当依赖性检索,类是负责对获得自己的依赖。
从某种意义上来说,依赖注入更为纯净,因为注入的类将在构造过程中传递其依赖,因此对DI一无所知。但是,它比较麻烦,并且不提供很多功能。
相反,依赖检索更容易且功能齐全,但是它要求类了解DI。

注射
如果要注入类,则需要在构造时声明其依赖项:
示例:在构造上具有依赖关系的类

class Presenter(private val db: Database, private val rnd: Random) {
}

现在,您需要能够创建此类的新实例Presenter。使用Kodein-DI,这非常容易:
示例:创建一个注入类的对象
val presenter by di.newInstance { Presenter(instance(), instance()) }
对于Presenter构造函数的每个参数,您都可以简单地使用该instance()函数,而Kodein-DI实际上将传递正确的依赖关系。
Retrieval
使用检索时,该类需要可用于静态或通过参数访问DI实例。在这些示例中,我们将使用参数。
示例:一个检索其自身依赖关系的类

class Presenter(val di: DI) {
    private val db: Database by di.instance()
    private val rnd: Random by di.instance()
}

注意by关键字的用法。使用依赖关系检索时,会延迟检索属性。
您可以进一步使用该DIAware界面,该界面可以解锁许多功能:

示例:KodeinAware类 或是DIAware

class Presenter(override val di: DI): DIAware{
    private val db: Database by instance()
    private val rnd: Random by instance()
}

请注意,由于默认情况下所有内容都是惰性的,因此对类中DI对象的访问KodeinAware本身可以是惰性的:
示例:带有惰性DI的KodeinAware类

class Presenter(): KodeinAware {
    override val di by lazy { getApplicationContext().di }

    private val db: Database by instance()
    private val rnd: Random by instance()
}

Retrieval:direct
如果您不想使用委托的属性,则可以使用Kodein-DI。大多数可用的功能DI可供DirectDI。 DirectDI使您可以直接获取新实例或依赖项。
然而,因为它是直接的,DirectDI也不要特点:
• 惰性:在调用时获取实例/提供者/工厂。
• 接收者意识:接收者是由Kotlin的委托属性机制定义的。
示例:使用DirectDI

val directDI = di.direct

val ds: Datasource = directDI.instance()

val controller = directDI.newInstance { MainController(instance(), instance(tag = "whatever")) }

示例:使用DirectDI

val di = DI.direct { 
        /* bindings */
    }

传递依存关系
假设我们要Provider在绑定中声明。它具有自己的依赖性。依赖性的依赖性是传递性依赖性。处理这些依赖关系实际上非常容易。
如果使用注入,则可以完全相同的方式传递参数:
示例:将Presenter类与注入绑定

val di = DI {
    bind<Presenter>() with singleton { Presenter(instance(), instance()) }
}

如果使用检索,只需传递di属性:
示例:将Presenter类与注入绑定

val di = DI {
    bind<Presenter>() with singleton { Presenter(di) }
}

平台兼容性和通用性
Kodein-DI 7.4.0建立在Kotlin 1.4.31之上并与之兼容。
另外,对于JVM项目,它至少需要JDK 1.8。

Kodein-DI在Ktor上

安装

implementation(“org.kodein.di:kodein-di-framework-ktor-server-jvm:7.1.0”)

DIFeature
由于Ktor应用程序基于扩展,因此我们无法DIAware在其上使用该机制。因此,我们必须找到另一种优雅的方式来提供全局DI容器。那就是DIFeature(通过扩展功能Application.di())。
示例:Ktor应用程序声明,安装 DIFeature

fun main(args: Array<String>) {
    embeddedServer(Netty, port = 8080) {
        di { 
            bind<Random>() with singleton { SecureRandom() } 
        }
   }.start(true)
}

示例:Ktor应用程序声明,安装DIFeature,然后从路由中检索它

fun main(args: Array<String>) {
    embeddedServer(Netty, port = 8080) {
        di { 
            bind<Random>() with singleton { SecureRandom() } 
        }

        routing {
            get("/") {
                val random by di().instance<Random>() 
                /* logic here */
            }
        }
   }.start(true)
}

subDI为Routing/Route类提供了一个功能,仅在Route及其子项中可用

  route("/login") {
                subDI {
                    bind<CredentialsDao> with singleton { CredentialsDao() } 
                }
}

复制绑定

subDI(copy = Copy.All) {
/** new bindings / overrides **/
}

覆盖绑定

//覆盖Foo绑定
subDI {
    bind<Foo>(overrides = true) with provider { Foo("explicitFoo") } 
}
//覆盖subDI将是隐式的
subDI(allowSilentOverrides = true) { 
    bind<Foo> with provider { Foo("implicitFoo") }
}

subDI的扩展父实例
此功能仅限于Routing/ Route,可以像以下方式使用:
示例:从多个地方扩展
// https://ktor.io/servers/features/routing.html[Routing]

routing {
    /* usage */
    val subDI = subDI { /** new bindings / overrides **/ } //1扩展最近的DI实例,很可能是应用程序的实例
    route("/books") {
        /* usage */
        subDI { /** new bindings / overrides **/ } //2扩展最近的DI实例,即在<1>中创建的实例

        route("/author") {
            /* usage */
            subDI { /** new bindings / overrides **/ } //3扩展最近的DI实例,即在<2>中创建的实例
        }
    }
}

Ktor scopes

Session scopes
1.通过实现定义会话 DISession
示例:定义会话

data class UserSession(val user: User) : KodeinDISession { 
    override fun getSessionId() = user.id 
}
创建实现的会话对象 KodeinDISession
实现功能 getSessionId()

2.定义范围依赖
示例:定义会话范围的依赖项

fun main(args: Array<String>) {
    embeddedServer(Netty, port = 8000) {
        install(Sessions) { 
            cookie<UserSession>("SESSION_FEATURE_SESSION_ID") 
        }
        di {
            bind<Random>() with scoped(SessionScope).singleton { SecureRandom() } 
            /* binding */
        }
    }.start(true)
}

安装Sessions功能
声明以表示的会话cookie UserSession
绑定Random对象的范围SessionScope
3.检索范围内的依赖项
示例:检索会话范围的依赖项

embeddedServer(Netty, port = 8000) {
    /* configurations */
    routing {
        get("/random") {
            val session = call.sessions.get<UserSession>() ?: error("no session found!") 
            val random by di().on(session).instance<Random>() 
            call.responText("Hello ${session.user.name}, your random number is ${random.nextInt()}")
        }
    }
}.start(true)
session从请求上下文中检索,否则将失败
Random从DI范围为以下对象的对象中检索对象session

4.只要不再使用会话,请清除作用域
示例:清除会话和作用域
get("/clear") {
call.sessions.clearSessionScope()// 清除会话并删除ScopeRegistry链接到该会话的链接
}

Call scope

val di = DI {
    bind<Random>() with scoped(CallScope).singleton { SecureRandom() } 
}

Random将为每个请求(HTTP或Websocket)创建一个对象,只要该请求存在,就将对其进行检索。

get {
    val random by di().on(context).instance<Random>()
}

DI Controllers

安装
implementation(“org.kodein.di:kodein-di-framework-ktor-server-controller-jvm:7.4.0”)

在kodein-di-framework-ktor-server-controller-jvm已经拥有kodein-di-framework-ktor-server-jvm的传递依赖,所以你不需要声明两种。

class MyController(application: Application) : DIController { 
override val di by di { application } 
//implement 从 DIaware中override di
    private val repository: DataRepository by instance("dao") 

    override fun Route.getRoutes() { 
        get("/version") { 
            val version: String by instance("version") 
            call.respondText(version)
        }
    }
}

1实现DIController并提供Application实例(来自构造函数)
2DI根据提供的内容覆盖容器Application
3DI像任何DIAwareclass一样使用您的容器
4覆盖功能Route.getRoutes并定义一些路线
5该路线将由 DIControllerFeature
6DI像任何DIAwareclass一样使用您的容器
扩展AbstractDIController

class MyController(application: Application) : AbstractDIController(application) { 
    private val repository: DataRepository by instance("dao") 

    override fun Routing.installRoutes() { 
        get("/version") { 
            val version: String by instance("version") 
            call.respondText(version)
        }
    }
}

1扩展AbstractDIController并提供Application实例(来自构造函数)
2DI像任何DIAwareclass一样使用您的容器
3覆盖功能Routing.installRoutes并定义一些路线
4该路线将由 DIControllerFeature
5DI像任何DIAwareclass一样使用您的容器
使用DIController或AbstractDIController取决于您的需求。
如果您不需要在控制器上使用继承,那么您可以从中受益AbstractDIController。
相反,如果要对控制器使用继承,则应自己实现DIController和覆盖DI容器。

public abstract class AbstractDIController(private val application: Application) : DIController {
    public override val di: DI by di { application }
}

以使用Route.controller扩展功能。这些功能将自动将您定义的路由安装DIController到Ktor路由系统中

controller("/protected") { MySecondDIController(instance()) }

测试代码

val kodein = DI{
    bind<Int>(tag = "demo") with provider { Random().nextInt(111) }
    bind<Random>() with singleton { Random() }
    bind<presenter2>() with singleton { presenter2(instance()) }
}

class presenter(kodein:DI){
    public val rnd: Int by kodein.instance(tag = "demo")
}

class presenter2( private val rnd: Random) {
    fun println(){
        println(rnd.nextInt())
    }
}
class presenter3(override val di: DI):DIAware{
    public val rnd: Random by instance()
}
fun main(args: Array<String>) {


//    println(presenter.rnd.nextInt(10))
    println(presenter(kodein).rnd)
    println(presenter(kodein).rnd)
    val presenter2 by kodein.newInstance { presenter2(instance()) }
    presenter2.println()
    println(presenter3(kodein).rnd.nextInt())

    val presenter3 by kodein.instance<presenter2>()
    presenter3.println()
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KtorKotlin 官方提供的一个轻量级、灵活的 Web 框架,它可以用于开发 Web 应用程序、API、微服务等。在 Kotlin for Desktop ,我们可以使用 Ktor 构建和部署桌面应用程序的后端,以提供数据和服务。 下面是一个简单的 Kotlin for Desktop Ktor使用示例: ```kotlin import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.* import io.ktor.server.engine.* import io.ktor.server.netty.* fun main() { embeddedServer(Netty, port = 8080) { routing { get("/") { call.respondText("Hello, world!") } } }.start(wait = true) } ``` 这个示例创建了一个 Ktor 应用程序,监听 8080 端口,并且在访问根路径时返回 "Hello, world!" 字符串。可以在浏览器或者其他 HTTP 客户端访问 http://localhost:8080/ 查看结果。 需要注意的是,在 Kotlin for Desktop 使用 Ktor 可能需要添加额外的依赖项,例如: ```kotlin dependencies { implementation("io.ktor:ktor-server-netty:$ktor_version") implementation("io.ktor:ktor-server-core:$ktor_version") implementation("io.ktor:ktor-server-host-common:$ktor_version") implementation("io.ktor:ktor-serialization:$ktor_version") } ``` 其 $ktor_version 是 Ktor 的版本号,可以根据需要进行修改。另外,还需要在项目的 build.gradle 或 settings.gradle 添加 Maven 仓库: ```kotlin repositories { mavenCentral() } ``` 通过使用 Ktor,我们可以很方便地在 Kotlin for Desktop 构建和部署后端服务,实现应用程序的完整功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值