容器服务

容器服务

正如我们在 IoC 容器指南中所讨论的,容器绑定是 AdonisJS 中 IoC 容器存在的主要原因之一。

容器绑定使你的代码库免于在使用对象之前需要编写的样板代码,从而保持其整洁。

在以下示例中,在使用 Database 类之前,你必须创建其实例。根据你正在构造的类,你可能需要编写大量样板代码来获取其所有依赖项。

import { Database } from '@adonisjs/lucid'
export const db = new Database(
// 注入配置和其他依赖项
)

然而,当使用 IoC 容器时,你可以将构造类的任务丢给容器,并获取一个预构建的实例。

import app from '@adonisjs/core/services/app'
const db = await app.container.make('lucid.db')

容器服务的必要性

使用容器来解析预配置的对象是很好的。然而,使用 container.make 方法也有其缺点。

  • 编辑器擅长自动导入。如果你尝试使用一个变量,并且编辑器可以猜测该变量的导入路径,那么它将为你编写导入语句。但是,这不能用于 container.make 调用。

  • 与使用统一的语法来导入/使用模块相比,混合使用导入语句和 container.make 调用显得不直观。

为了克服这些缺点,我们将 container.make 调用包装在一个常规的JavaScript模块中,这样你就可以使用 import 语句来获取它们。

例如,@adonisjs/lucid 包有一个名为 services/db.ts 的文件,该文件大致包含以下内容。

const db = await app.container.make('lucid.db')
export { db as default }

在你的应用程序中,你可以将 container.make 调用替换为指向 services/db.ts 文件的导入。

import app from '@adonisjs/core/services/app'
const db = await app.container.make('lucid.db')
import db from '@adonisjs/lucid/services/db'

如你所见,我们仍然依赖容器来为我们解析 Database 类的实例。然而,通过一层间接,我们可以用常规的 import 语句替换 container.make 调用。

包装 container.make 调用的 JavaScript 模块被称为容器服务。 几乎每个与容器交互的包都会附带一个或多个容器服务。

容器服务与依赖注入

容器服务是依赖注入的一种替代方案。例如,你不是将 Disk 类作为依赖项接受,而是请求 drive 服务为你提供一个磁盘实例。让我们看一些代码示例。

在以下示例中,我们使用 @inject 装饰器来注入 Disk 类的实例。

import { Disk } from '@adonisjs/drive'
import { inject } from '@adonisjs/core'
@inject()
export class PostService {
constructor(protected disk: Disk) {
}
async save(post: Post, coverImage: File) {
const coverImageName = 'random_name.jpg'
await this.disk.put(coverImageName, coverImage)
post.coverImage = coverImageName
await post.save()
}
}

当使用 drive 服务时,我们调用 drive.use 方法来获取带有 s3 驱动程序的 `Disk 实例。

import drive from '@adonisjs/drive/services/main'
export class PostService {
async save(post: Post, coverImage: File) {
const coverImageName = 'random_name.jpg'
const disk = drive.use('s3')
await disk.put(coverImageName, coverImage)
post.coverImage = coverImageName
await post.save()
}
}

容器服务有助于保持代码简洁。而依赖注入则在不同应用程序部分之间创建了松耦合。

选择哪一个取决于你的个人偏好以及你希望采用的代码结构方法。

// test:

使用容器服务进行测试

依赖注入的直接好处是在编写测试时能够替换依赖项。

为了在容器服务中提供类似的测试体验,AdonisJS在编写测试时提供了用于伪造实现的一流API。

在以下示例中,我们调用 drive.fake 方法来用内存驱动程序替换驱动磁盘。创建伪造后,对 drive.use 方法的任何调用都将接收伪造实现。

import drive from '@adonisjs/drive/services/main'
import { PostService } from '#services/post_service'
test('save post', async ({ assert }) => {
/**
* 伪造s3磁盘
*/
drive.fake('s3')
const postService = new PostService()
await postService.save(post, coverImage)
/**
* 编写断言
*/
assert.isTrue(await drive.use('s3').exists(coverImage.name))
/**
* 恢复伪造
*/
drive.restore('s3')
})

容器绑定与服务

下表列出了框架核心和第一方包导出的容器绑定及其相关服务。

绑定 服务
app Application @adonisjs/core/services/app
ace Kernel @adonisjs/core/services/kernel
config Config @adonisjs/core/services/config
encryption Encryption @adonisjs/core/services/encryption
emitter Emitter @adonisjs/core/services/emitter
hash HashManager @adonisjs/core/services/hash
logger LoggerManager @adonisjs/core/services/logger
repl Repl @adonisjs/core/services/repl
router Router @adonisjs/core/services/router
server Server @adonisjs/core/services/server
testUtils TestUtils @adonisjs/core/services/test_utils