选项配置
选项配置(Options)是一种类型安全、统一、规范的应用程序配置方式,适用于多架构、多租户场景。它是依赖注入的重要扩展,帮助你将配置与业务逻辑解耦,提高代码的可维护性和使用的便利性。统一模块开发者和使用者之间的开发和使用习惯,简化模块上手的复杂度和可配置性。
快速启动
只需 5 步即可开始使用选项配置:
cangjie
import spire_extensions_injection.*
// 1. 定义选项类型
public class ApplicationOptions {
var name = ""
}
main() {
// 2. 创建服务集合
let services = ServiceCollection()
// 3. 注册选项配置
services.configure<ApplicationOptions>({
configureOptions => configureOptions.name = "spire"
})
// 4. 构建服务提供者
let provider = services.build()
// 5. 解析并使用选项
let options = provider.getOrThrow<IOptions<ApplicationOptions>>()
options.value.name |> println // spire
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
配置选项
命名选项
支持多租户或多配置场景,可以为不同名称注册不同配置:
cangjie
import spire_extensions_injection.*
main() {
// 1. 创建服务集合
let services = ServiceCollection()
// 2. 注册多租户选项配置(默认、tenant1、tenant2)
services.configure<ApplicationOptions>({
configureOptions => configureOptions.name = "default"
})
services.configure<ApplicationOptions>("tenant1", {
configureOptions => configureOptions.name = "tenant1"
})
services.configure<ApplicationOptions>("tenant2", {
configureOptions => configureOptions.name = "tenant2"
})
// 3. 构建服务提供者并获取选项监控器
let provider = services.build()
let optionsMonitor = provider.getOrThrow<IOptionsMonitor<ApplicationOptions>>()
// 4. 分别获取并打印不同租户的选项值
optionsMonitor.currentValue.name |> println // default
optionsMonitor.get("tenant1").name |> println // tenant1
optionsMonitor.get("tenant2").name |> println // tenant2
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
注意
命名选项通过解析 IOptionsMonitor<TOptions>
来读取
配置后置处理(configureAfter)
有时需要在所有 configure
配置完成后再进行一次统一处理(例如你需要修改框架的既有配置),可以使用 configureAfter
进行配置覆盖:
cangjie
services.configureAfter<ApplicationOptions>({configureOptions =>
configureOptions.name = "cangjie"
})
services.configure<ApplicationOptions>({configureOptions =>
configureOptions.name = "spire"
})
// ...
let options = provider.getOrThrow<IOptions<ApplicationOptions>>()
options.value.name |> println // cangjie
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
批量配置(configureAll)
对所有命名的选项统一生效,适合做全局默认或统一收尾:
cangjie
import spire_extensions_injection.*
public class ApplicationOptions { var name = "" }
main() {
let services = ServiceCollection()
// 为不同名称先注册各自配置
services.configure<ApplicationOptions>("tenant1", { o => o.name = "t1" })
services.configure<ApplicationOptions>("tenant2", { o => o.name = "t2" })
// 对所有命名统一设置(后置阶段)
services.configureAll<ApplicationOptions>({ options => options.name = "all" })
// 全局最终覆盖(后置阶段)
services.configureAfterAll<ApplicationOptions>({ options => options.name = "after-all" })
let provider = services.build()
let monitor = provider.getOrThrow<IOptionsMonitor<ApplicationOptions>>()
monitor.get("tenant1").name |> println // after-all
monitor.get("tenant2").name |> println // after-all
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
顺序
所有 configure
先执行,随后执行所有 configureAfter
;configureAll
与 configureAfterAll
在当前实现中都属于“后置阶段”,统一作用于所有命名。
配置校验
配置校验(validate)
配置完成后,可以调用 validate
方法对配置项进行校验,校验失败将会抛出异常
cangjie
import spire_extensions_injection.*
class TestOptions { var version: Int32 = 0 }
main() {
let services = ServiceCollection()
services.addOptions<TestOptions>("tenant1")
.configure { configureOptions => configureOptions.version = 1 }
.validate { options => return options.version > 1 }// 校验配置版本是否大于1
let provider = services.build()
let monitor = provider.getOrThrow<IOptionsMonitor<TestOptions>>()
try {
// 获取配置时抛出异常
let _ = monitor.get("tenant1")
println("An error should be catched here!")
} catch (ex: OptionsValidationException) {
println("Catch error success, validate success!")
} catch (ex: Exception) {
println("Catch error fails, validate fails!")
}
// output: Catch error success, validate success!
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
集成依赖注入
选项配置与依赖注入结合,可以轻松实现配置驱动的服务,下面是一个消息队列处理器的场景:
cangjie
import std.time.*
import spire_extensions_injection.*
// 定义选项类型,默认延迟为10秒
public class WorkServiceOptions {
var delay = 10
}
// 定义服务类,通过构造函数注入选项
public class WorkService {
// 在服务类的构造函数中声明 IOptions<T> 参数,框架会自动将选项对象注入
public WorkService(let options: IOptions<WorkServiceOptions>){}
// 服务逻辑
public func working() {
while (!Thread.currentThread.hasPendingCancellation) {
print("WorkService is working\n")
sleep(Duration.second * options.value.delay)
}
print("WorkService working end")
}
}
// 扩展方法,简化服务注册
extend ServiceCollection {
public func addWorkService() {
this.addOptions<WorkServiceOptions>()
this.addSingleton<WorkService, WorkService>()
}
}
main() {
let services = ServiceCollection()
services.addWorkService()
// 后配置修改默认延迟为1秒
services.configureAfter<WorkServiceOptions>{options =>
options.delay = 1
}
let provider = services.build()
// 解析选项并获取服务
let work = provider.getOrThrow<WorkService>()
work.working()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
总结
通过 addOptions<T>()
方法注册选项类型,然后服务可以通过构造函数注入 IOptions<T>
来获取配置,同时应用后配置修改默认延迟为 1s
实现了服务逻辑与配置解耦
常见用法
- 类型安全:所有选项均为强类型,避免魔法字符串。
- 依赖注入集成:选项对象可直接注入到服务中。
- 后置处理:通过
configureAfter
实现统一收尾配置。 - 命名选项:支持多租户/多环境配置。
最佳实践
- 推荐将所有选项类型集中定义,便于管理和维护。
- 配置逻辑建议拆分为多个
configure
,最后用configureAfter
统一收尾。 - 命名选项适合多租户、分环境等复杂场景。
- 选项对象建议只读,避免运行时被修改。