路人战队|Facebook 在 Golang 依赖注入的实现( 二 )

  • 解析 UserApi 时 , 发现其依然有也有两个标记为 inject 的字段: UserService 和 Conf。 对于 UserService 这种 Graph 中未登记过的类型 , 会自动 Provide 。 而对 Conf , Graph 中之前已经注册过了 , 因此直接将注册的对象赋值给该字段即可 。
  • 接下来就是继续逐步解析 , 直至没有tag为 inject 的字段 。
  • 以上就是整个依赖注入的流程了 。
    这里需要注意的是 , 在我们上面的示例中 , 以这种方式注入 , 其中所有的对象都相当于单例对象 。 即一个类型 , 只会在 Graph 中存在一个实例对象 。 比如 UserController 和 PosterController 中的 UserService 实际上是同一个对象 。
    我们的 main 函数使用 inject 进行改造后 , 将会变得非常简洁 。 而且即使随着业务越来越复杂 , Handler 和 Service 越来越多 , 这个 main 函数中的注入逻辑也不会任何改变 , 除非有新的根节点对象出现 。
    当然 , 对于 Graph 来说 , 也不是只能 Provide 根节点和叶子节点 , 我们也可以自行 Provide 一个 UserService 的实例进去 , 对于 Graph 的运作是没有任何影响的 。 只不过只 Provide 根节点和叶子节点 , 代码会更简洁一些 。
    inject 的高级用法我们在声明 tag 时 , 除了声明为 inject:"" 这种默认用法外 , 还可以有其他三种高级的用法:
    inject:"private"inject:"inline"inject:"object_name"private (私有注入)我们上文讲过 , 默认情况下 , 所有的对象都是单例对象 。 一个类型只会有一个实例对象存在 。 但也可以不使用单例对象 , private 就是提供了这种可能 。
    例如:
    type UserController struct {UserService *UserService `inject:"private"`Conf*Conf`inject:""`}我们将 UserController 中的 UserService 属性声明为 private 注入 。 这样的话 , graph 遇到 private 标签时 , 会自动的 new 一个全新的 UserService 对象 , 将其赋值给该字段 。
    这样 Graph 中就同时存在了两个 UserService 的实例 , 一个是 UserService 的全局实例 , 给默认的 inject:"" 使用 。 一个是专门给 UserController 实例中的 UserService 使用 。
    但在实际开发中 , 这种 private 的场景似乎也比较少 , 大部分情况下 , 默认的单例对象就足够了 。
    inline (内联注入)默认情况下 , 需要注入的属性必须得是 *Struct。 但是也是可以声明为普通对象的 。 例如:
    type UserController struct {UserService UserService `inject:"inline"`Conf*Conf`inject:""`}注意 , 这里的 UserService 的类型 , 并非是 *UserService 指针类型了 , 而是普通的 struct 类型 。 struct 类型在 Go 里面都是值语义 , 这里当然也就不存在单例的问题了 。
    命名注入如果我们需要对某些字段注入专有的对象实例 , 那么我们可能会用到命名注入 。 使用方法就是在 inject 的 tag 里写上专有的名字 。 如下:
    type UserController struct {UserService UserService `inject:"named_service"`Conf*Conf`inject:""`}当然 , 这个命名肯定不能命名为 private 和 inline, 这两个属于保留词 。
    同时 , 我们一定要把这个命名实例 Provide 到 graph 里面 , 这样 graph 才能把两个对象联系起来 。
    graph.Provide(注入 map我们除了可以注入对象外 , 还可以注入 map 。 如下:
    type UserController struct {UserService UserService`inject:"inline"`Conf*Conf`inject:""`UserMapmap[string]string `inject:"private"`}需要注意的是 , map 的注入 tag 一定要是 inject:"private"。
    facebookgo/inject 的缺陷facebookgo/inject 固然很好用 , 只要声明 inject:"" 的 tag , 提供几个对象 , 就可以完全自动的注入所有依赖关系 。


    推荐阅读