配置提供者

配置服务提供者

某些配置文件(如 config/hash.ts)并不会将配置作为普通对象导出,而是导出一个配置服务提供者。配置服务提供者为软件包提供了一套透明的 API,可以在应用启动后懒计算配置。

不使用配置服务提供者

为了理解配置服务提供者,我们先看看如果不使用配置服务提供者,config/hash.ts 文件会是什么样子。

import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
export default {
default: 'scrypt',
list: {
scrypt: () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
})
}
}

到目前为止,一切顺利。我们没有从 drivers 集合中引用 scrypt 驱动,而是直接导入它,并使用工厂函数返回一个实例。

假设 Scrypt 驱动需要一个 Emitter 类的实例,以便在每次哈希一个值时发出一个事件。

import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
import emitter from '@adonisjs/core/services/emitter'
export default {
default: 'scrypt',
list: {
scrypt: () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
}
}

🚨 上面的示例会失败,因为 容器服务 在应用启动之前是不可用的,而 config 文件会在应用启动阶段之前被导入。

嗯,这是 AdonisJS 架构的问题吗 🤷🏻‍♂️

并非如此。我们不去使用容器服务,而是直接在 config 文件内创建 Emitter 类的实例。

import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
import emitter from '@adonisjs/core/services/emitter'
import { Emitter } from '@adonisjs/core/events'
const emitter = new Emitter()
export default {
default: 'scrypt',
list: {
scrypt: () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
}
}

现在,我们有了一个新问题。我们为 Scrypt 驱动创建的 emitter 实例并非全局可用,我们无法导入它并监听该驱动发出的事件。

因此,你可能想把 Emitter 类的构建移到它自己的文件里,并导出一个它的实例。这样,你就可以将 emitter 实例传递给驱动,并用它来监听事件。

start/emitter.ts
import { Emitter } from '@adonisjs/core/events'
export const emitter = new Emitter()
import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
import { Emitter } from '@adonisjs/core/events'
import { emitter } from '#start/emitter'
const emitter = new Emitter()
export default {
default: 'scrypt',
list: {
scrypt: () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
}
}

上面的代码可以正常工作。不过,这次你是手动构建了应用所需的依赖。结果就是,你的应用将充斥着大量样板代码来把一切连接起来。

使用 AdonisJS,我们力求编写最少的样板代码,并使用 IoC 容器来查找依赖。

使用配置服务提供者

现在,让我们重新编写 config/hash.ts 文件,这次使用一个配置服务提供者。配置服务提供者是一个花哨的名称,指代一个接受 Application 类实例 并使用容器解析其依赖的函数。

import { configProvider } from '@adonisjs/core'
import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
export default {
default: 'scrypt',
list: {
scrypt: configProvider.create(async (app) => {
const emitter = await app.container.make('emitter')
return () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
})
}
}

一旦你使用了 hash 服务,就会执行 scrypt 驱动的配置服务提供者来解析其依赖。因此,在我们于代码其他地方使用 hash 服务之前,我们不会去查找 emitter

由于配置服务提供者是异步的,你可能想通过动态导入懒加载 Scrypt 驱动。

import { configProvider } from '@adonisjs/core'
import { Scrypt } from '@adonisjs/core/hash/drivers/scrypt'
export default {
default: 'scrypt',
list: {
scrypt: configProvider.create(async (app) => {
const { Scrypt } = await import('@adonisjs/core/hash/drivers/scrypt')
const emitter = await app.container.make('emitter')
return () => new Scrypt({
cost: 16384,
blockSize: 8,
parallelization: 1,
maxMemory: 33554432,
}, emitter)
})
}
}

如何访问已解析的配置?

你可以直接从服务访问已解析的配置。例如,对于 hash 服务,你可以这样获取已解析配置的引用。

import hash from '@adonisjs/core/services/hash'
console.log(hash.config)