容器服务
正如我们在 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
|