如何在库中安全维护 Wire Provider Set:哪些修改不破坏现有 injector? 如何在库中安全维护 Wire Provider Set哪些修改不破坏现有 injector【免费下载链接】wireCompile-time Dependency Injection for Go项目地址: https://gitcode.com/GitHub_Trending/wi/wire当你的 Go 库通过wire.NewSet暴露 provider set供其他应用写进wire.Build时每次改动都可能影响消费方已生成的wire_gen.go。本文基于 Wire 项目文档中的最佳实践给出判断哪些修改与现有 injector 兼容、哪些会破坏兼容的标准并说明如何在发布前用wire命令实际验证改动对现有 injector 的影响。适用前提消费方项目已按 Wire 的方式定义了 injector函数体只含wire.Build调用并已在源码管理中提交了wire.go与wire_gen.go。先明确provider set 的输入/输出类型就是兼容边界Wire 的核心概念是 provider能产出某个类型的函数和 injector按依赖顺序调用 provider 的函数。库作者用wire.NewSet把一组 provider 打包成 provider set消费方在 injector 声明中引用它Wire 在代码生成阶段为 injector 填入实现输出到wire_gen.go见 docs/guide.md。对库的维护者来说有两点决定了什么改动是安全的Wire 通过类型标识匹配输入和输出provider set 暴露的输入类型集合与输出类型集合就是它对消费方的契约一个重要事实把 provider set 里某个输出对应的 provider 换成另一个函数后消费方现有的 injector 在重新生成之前仍在使用旧的 providerdocs/best-practices.md 明确指出 existing injectors will use the old provider until they are regenerated。也就是说wire_gen.go不会随库升级自动更新改动是否破坏要分两层看能否让消费方重新生成成功以及不重新生成时行为是否可接受。哪些修改是安全的docs/best-practices.md 给出了明确的判断标准。在不破坏兼容性的前提下库里的 provider set 只允许两类修改1. 更换某个已有输出的 provider但不引入新的输入条件新 provider 产出的类型与原来相同新 provider不得引入新的输入类型参数类型集合不能变大允许减少输入移除输入是安全的注意上面提到的限制消费方在重新生成 injector 之前仍走旧 provider。2. 向 provider set 中引入一个全新的输出类型条件该输出类型必须是新引入的类型而不是某个已经存在的类型文档要求该类型与提供它的 provider 在同一个 commit/发布中一起引入避免消费方拿到provider 已发布、类型尚未发布的中间状态。判断是否已有输出类型时冲突的真实形态是 Wire 的multiple bindings错误。项目测试数据internal/wire/testdata/MultipleBindings/want/wire_errs.txt示例结果其中的x:y是占位位置展示了这类错误的样子example.com/foo/wire.go:x:y: multiple bindings for example.com/foo.Foo current: - provider provideFooAgain (example.com/foo/foo.go:x:y) previous: - provider provideFoo (example.com/foo/foo.go:x:y)只要消费方的 provider set 里已存在同一类型的 provider无论来自另一个 provider set、wire.Value还是wire.Bind再给这个类型加 provider 就会触发该错误。这也是下一条不安全修改会破坏 injector 的原因。哪些修改会破坏现有 injector除上述两类外其余修改都不安全docs/best-practices.md要求新的输入provider set 新增一个只有消费方才需要提供值的类型消费方的 injector 必须重新生成并补齐该输入否则生成失败移除某个输出类型依赖该输出的消费方 injector 会直接坏掉把一个已存在的输出类型加进 provider set可能和消费方已有的 provider 冲突产生multiple bindings错误。文档给出的替代方案是新增一个 provider set而不是修改现有 set。下面这段代码是 docs/best-practices.md 中的官方示例完整展示了可以做什么、不可以做什么Greeter、Message等为示例类型var GreeterSet wire.NewSet(NewStdoutGreeter) func DefaultGreeter(ctx context.Context) *Greeter { // ... } func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter { // ... } func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) { // ... }对照这个 set允许在GreeterSet中用DefaultGreeter替换NewStdoutGreeter——两者都不引入新输入允许新建一个类型T并为它添加 provider 放入GreeterSet只要T与 provider 在同一个 commit/发布中引入不允许在GreeterSet中用NewGreeter替换NewStdoutGreeter——它新增了输入类型io.Writer并且使*Greeter的 provider 开始返回error消费方的 injector 签名需要相应变化不允许从GreeterSet中移除NewStdoutGreeter——依赖*Greeter的消费方 injector 会坏掉不允许给GreeterSet添加io.Writer的 provider——消费方可能已经有io.Writer的 provider会冲突。如何验证一次修改不会破坏现有 injector验证动作发生在消费方的项目里库代码修改后在引用了该 provider set 的 injector 所在包中操作。wire命令行工具提供了几个子命令其行为以 cmd/wire/main.go 中的 usage 说明为准均支持[packages]参数未指定包时默认.。先确认工具可用。按 README.md 安装并确保$GOPATH/bin在$PATH中go install github.com/google/wire/cmd/wirelatest第一步用wire diff预览对现有 wire_gen.go 的影响diff子命令generates the content for their wire_gen.go files and outputs the diff against the existing files即生成将写入的内容并与现有文件对比输出不修改文件适合在改动后先做检查。按 usage 说明它returns 0 if no diff, 1 if different, 2 plus an error if troublewire diff ./...退出码 0 且无输出库修改后重新生成结果与原wire_gen.go一致现有 injector 不受影响退出码 1 并输出 unified diff生成结果有变化消费方需要重新生成wire_gen.go。此时对照 diff 内容核对变化是否属于前文安全修改的预期例如只是 provider 函数被替换而按文档说明旧 injector 在重新生成前仍使用旧 provider退出码 2加载或生成出错按输出的错误信息排查。第二步用wire check检查类型与 Wire 错误check子命令prints any type-checking or Wire errors found with top-level variable provider sets or injector functionswire check ./...如果消费方的wire.Build与新版本的 provider set 组合后存在multiple bindings、缺少 provider 等问题会在这里以错误形式打印出来教程中展示了缺 provider 时的错误示例见 _tutorial/README.md。没有输出错误即表示该包内 provider set 与 injector 声明通过了 Wire 的检查。第三步确认可接受后在消费方重新生成重新生成有两条等价路径docs/guide.md# 在包含 injector 的包目录内直接运行 wire # 或者依赖生成文件中的指令 go generate生成产物写入wire_gen.go如 wire 生成的示例文件 所示文件头带有// Code generated by Wire. DO NOT EDIT.。wire_gen.go头部包含//go:generate go run -modmod github.com/google/wire/cmd/wire因此go generate可以持续再生成它。如需了解某个包中顶层 provider set 导入了哪些 set、在各输入条件下能产出哪些输出类型可以用wire show [packages]查看cmd/wire/main.go 中的 usage列出 provider set 的 imports、Outputs given 以及包内定义的 injector 函数适合在改动前记录 set 的输入/输出基线与改动后的输出对照判断是否落入要求新的输入 / 移除输出 / 加入已有输出这三类破坏性修改。设计库 provider set 时的配套建议best-practices 文档同时给出了从源头降低破坏概率的设计建议保持库的 provider set 小而窄文档建议库 set 通常只包含单个 provider 函数外加一条wire.Bind把返回类型绑定到它实现的接口。docs/guide.md 补充了配套约束Any set that includes an interface binding must also have a provider in the same set that provides the concrete type——含wire.Bind的 set 必须在同一 set 内提供对应具体类型的 provider。不要把通用类型打包进库 set文档以 web 服务客户端为例说明虽然很有吸引力但不要在自己的客户端 provider set 里捆绑*http.Client的 provider因为每个库都这么做时彼此会冲突正确做法是 set 只包含 API 客户端的 provider把*http.Client留作 set 的输入由消费方提供。需要多个同类型依赖时定义新类型如 docs/faq.md 所示同一类型只能有一个 provider合法的同类型多依赖场景应发明新类型如type OtherFoo Foo再包装/解包避免触发multiple bindings。限制与说明兼容规则只覆盖换 provider 且不新增输入新增全新输出类型两类修改换 provider 时消费方的旧wire_gen.go在重新生成前不会自动切换到新 provider行为差异由库作者自己评估。按 README.md 的项目状态说明Wire 自 v0.3.0 起为 beta 且功能完整项目方表示不再接受新特性仓库已声明不再维护扩展需在 fork 中进行。本文的维护规则以仓库当前文档为准。wire diff、wire check、wire show的子命令行为取自 cmd/wire/main.go 中注册的命令 usage 文本可用wire help或wire commands在本地查看完整列表。【免费下载链接】wireCompile-time Dependency Injection for Go项目地址: https://gitcode.com/GitHub_Trending/wi/wire创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考