Skip to content

选项配置

选项配置(Options)是一种类型安全、统一、规范的应用程序配置方式,适用于多架构、多租户场景。它是依赖注入的重要扩展,帮助你将配置与业务逻辑解耦,提高代码的可维护性和使用的便利性。统一模块开发者和使用者之间的开发和使用习惯,简化模块上手的复杂度和可配置性。

快速启动

只需 5 步即可开始使用选项配置:

cangjie

import spire_extensions_options.*
import spire_extensions_injection.*

// 1. 定义选项类型
public class ObsOptions {
    public var accessKey = ""
    public var accessSecret = ""
}

main() {
    // 2. 创建服务集合 
    let services = ServiceCollection()

    // 3. 注册选项配置 
    services.configure<ObsOptions>({ options => 
        options.accessKey = "YWJjZGVmZ2hpamsxMjM0NTY3ODk"
        options.accessSecret = "d0phbHJYVXRuRkVNSS9LN01ERU5HL2JQeFJmaUNZRVhBTVBMRUtFWQ=="
    })

    // 4. 构建服务提供者 
    let provider = services.build()

    // 5. 解析并使用选项 
    let options = provider.getOrThrow<IOptions<ObsOptions>>()
    options.value.accessKey |> println
    options.value.accessSecret |> println
}

提示

选项是单例的,使用configure方法配置选项会将配置函数IOptions<T>,注册到容器中。首次解析选项时会使用配置函数来配置选项。

配置选项

选项模块扩展了依赖注入模块,并且提供了大量的配置函数。

注册选项

addOptions:注册选项

cangjie
// 只注册,不配置
services.addOptions<ObsOptions>()

此时可以从容器解析IOptions<ObsOptions>

配置选项

configure:前置配置

cangjie

// 注册,并配置
services.configure<ObsOptions>{options =>
    options.accessKey = "xxx"
}
// 可以有多个配置方法
services.configure<ObsOptions>{options =>
    options.accessKey = "YWJjZGVmZ2hpamsxMjM0NTY3ODk"
}

同一个选项,可以有多个configure,多个configure方法之间,按注册先后顺序执行。

configureAfter:后置配置

cangjie

// 注册,并配置
services.configureAfter<ObsOptions>{options =>
    options.accessKey = "xxxx"
}
// 可以多次配置
services.configure<ObsOptions>{options =>
    options.accessKey = "YWJjZGVmZ2hpamsxMjM0NTY3ODk"
}

configureAfter会在所有configure方法执行完成之后在执行。多个configureAfter之间按注册先后顺序执行

构建选项

OptionsBuilder:注册并配置选项

cangjie
// 注册,并配置选项
services.addOptions<ObsOptions>()
    .configure{ options =>
        options.accessKey = "YWJjZGVmZ2hpamsxMjM0NTY3ODk"
    }

命名选项

cangjie
let services = ServiceCollection()
// 租户1
services.configure<ObsOptions>("tenant1"){ options =>
    options.accessKey = "QUtJQUlPU0ZPRE5ON0VYQU1QTEU"
}
// 租户2
services.configure<ObsOptions>("tenant2"){ options =>
    options.accessKey = "YWJjZGVmZ2hpamsxMjM0NTY3ODk"
}
let provider = services.build()
let options = provider.getOrThrow<IOptionsMonitor<ObsOptions>>()
println(options.get("tenant1").accessKey)
println(options.get("tenant2").accessKey)

命名选项在多租户场景下非常有用

configureAll:忽略名称前置配置

忽略名称配置所有同类型的选项

cangjie
let services = ServiceCollection()
services.configure<ObsOptions>("tenant1"){ options =>
    options.accessKey = "QUtJQUlPU0ZPRE5ON0VYQU1QTEU"
}
services.configure<ObsOptions>("tenant2"){ options =>
    options.accessKey = "YWJjZGVmZ2hpamsxMjM0NTY3ODk"
}
// 忽略名称,配置所有的ObsOptions
services.configureAll<ObsOptions>{options =>
    options.accessKey = "xxx"
}
let provider = services.build()
let options = provider.getOrThrow<IOptionsMonitor<ObsOptions>>()
println(options.get("tenant1").accessKey)
println(options.get("tenant2").accessKey)

configureAll用于配置所有类型相同名称不同的选项,执行顺序同configure

configureAfterAll:忽略名称后置配置

cangjie
let services = ServiceCollection()
services.configureAfterAll<ObsOptions>{options =>
    options.accessKey = "xxx"
}
services.configure<ObsOptions>("tenant1"){ options =>
    options.accessKey = "QUtJQUlPU0ZPRE5ON0VYQU1QTEU"
}
services.configure<ObsOptions>("tenant2"){ options =>
    options.accessKey = "YWJjZGVmZ2hpamsxMjM0NTY3ODk"
}
let provider = services.build()
let options = provider.getOrThrow<IOptionsMonitor<ObsOptions>>()
println(options.get("tenant1").accessKey)
println(options.get("tenant2").accessKey)

configureAfterAll用于配置所有同类型的选项,无论是否是命名选项,执行顺序同configureAfter

配置校验

解析时验证

配置完成后,可以调用 validate 方法对配置项进行校验,校验失败将会抛出异常

cangjie
let services = ServiceCollection()
services.addOptions<ObsOptions>()
    .configure{options => 
        options.accessKey = "1234"
    }
    .validate("accessKey:长度必须大于5") { options =>
    options.accessKey.size > 5
}
let provider = services.build()
let options = provider.getOrThrow<IOptions<ObsOptions>>()
// tip:选项验证必须是第一次访问时才会触发
options.value.accessKey |> println

启动时验证

如果你希望在启动时验证某个选项,那么可以使用addOptionsWithValidateOnStart进行登记,启动时只需解析IStartupValidator来验证所有已登记的选项,这样就能在开发阶段规避错误。

cangjie
let services = ServiceCollection()
services.addOptions<ObsOptions>()
    .configure{options =>
        options.accessKey = "123"
    }
    .validate("accessKey:长度必须大于5") { options =>
    options.accessKey.size > 5
}
// 注册到IStartupValidator
services.addOptionsWithValidateOnStart<ObsOptions>()
let provider = services.build()
// IStartupValidator会检查所有已登记的选项
let validator = provider.getOrThrow<IStartupValidator>()
// 验证所有选项
validator.validate()