51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

源码分析——Go语言依赖注入库 samber/do

琢磨设计模式与抽象,可以说是我的最爱之一了。刚学 Go 的时候,我就陶醉于其的 interface 设计。

这次,我们来聊聊 Go 语言的依赖注入(DI)库 samber/do

本文不是一行行分析源码,而是尝试一步步复现作者的设计思路。

挖个坑先(为什么只讲 samber/do) {#挖个坑先为什么只讲-samberdo}

1. IoC 与 DI {#1-ioc-与-di}

首先,澄清一下,控制反转(IoC)与依赖注入(DI)是两个不同的概念。

(弄不清没关系,可以看这篇文章: 还没写

简单来说,控制反转是一种编程思想,而依赖注入是一种实现控制反转的方式。

2. 广义 IoC、DI,以及 DI Framework,以及 Go 的设计哲学 {#2-广义-iocdi以及-di-framework以及-go-的设计哲学}

其次,似乎一提到 IoC、DI 就必须提到 Spring,甚至很多人不用 Spring 举例子,就讲不清这两个概念。

换言之,很多人都弄不清,DI 与 DI Framework 的区别。

这又牵扯到另外一个问题, Go 是否需要 IoC, DI,还是说有着更适合 Go 的依赖倒置设计哲学?

3. DI 框架的众多实现 {#3-di-框架的众多实现}

且不说 DI 只是这些概念的一小部分,连 Go 的 DI 框架都有不少,例如:

总而言之

  • 因为一、二两点,导致在这之前,我应该写一篇只涉及编程思想的「论道」的文章。先欠着,由于篇幅和暂时的能力原因。
  • 因为第三点,我需要对比不同的 Go 语言的 DI 框架。
  • 所以,这篇文章,我只能先分析分析 samber/do 本身。

复现 samber/do 之路 {#复现-samberdo-之路}

先给 DI 来个狭义的定义吧:
狭义DI定义
对象的使用方式不应该依赖于对象的创建方式。

所以我们要实现的,就是:

  1. 提供一个「第三方」
  2. 对象创建者,把特定类型的对象创建出来并注册到第三方
  3. 对象使用者,从第三方获取对象

1. 需要知道的前置知识 {#1-需要知道的前置知识}

为了看懂本文,你需要知道:

  • Go 的基础语法
  • interface 与 泛型

2. 最简单的版本 {#2-最简单的版本}

原理也很简单,我们只要维护一个 map[reflect.Type]reflect.Value ,创建方(或者说 Provider )把对象创建出来,放到 map 里,使用方(调用方)从 map 里取出按类型取出对象即可。

Go 从 1.18 起,支持泛型,所以我们可以用泛型来简化代码。使用 map[string]any 来代替 map[reflect.Type]reflect.Value ,将类型名作为 key ,对象作为 value

代码如下,忽略了错误处理和并发安全:

                  
                    
                      
                        
                          package
                        
                        
                          main
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          import
                        
                        
                          (
                        
                      
                    
                    
                      
                        
                          "fmt"
                        
                      
                    
                    
                      
                        
                          "reflect"
                        
                      
                    
                    
                      
                        
                          )
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          type
                        
                        
                          Injector
                        
                        
                          struct
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          services
                        
                        
                          map
                        
                        
                          [
                        
                        
                          string
                        
                        
                          ]
                        
                        
                          any
                        
                      
                    
                    
                      
                        
                          s2
                        
                        
                          map
                        
                        
                          [
                        
                        
                          reflect
                        
                        
                          .
                        
                        
                          Type
                        
                        
                          ]
                        
                        
                          reflect
                        
                        
                          .
                        
                        
                          Value
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          func
                        
                        
                          New
                        
                        
                          ()
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          return
                        
                        
                          &
                        
                        
                          Injector
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          services
                        
                        
                          :
                        
                        
                          make
                        
                        
                          (
                        
                        
                          map
                        
                        
                          [
                        
                        
                          string
                        
                        
                          ]
                        
                        
                          any
                        
                        
                          ),
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          func
                        
                        
                          ProvideValue
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          ,
                        
                        
                          v
                        
                        
                          T
                        
                        
                          )
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          // ignore error handling

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          services
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          generateServiceName
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ]()]
                        </span>
                        <span class="p">
                          =
                        </span>
                        <span class="nx">
                          v
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="kd">
                          func
                        </span>
                        <span class="nx">
                          Invoke
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="nx">
                          any
                        </span>
                        <span class="p">
                          ](
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          Injector
                        </span>
                        <span class="p">
                          )
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                          // ignore error handling

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          services
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          generateServiceName
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ]()].(
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          )
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="kd">
                          func
                        </span>
                        <span class="nx">
                          generateServiceName
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="nx">
                          any
                        </span>
                        <span class="p">
                          ]()
                        </span>
                        <span class="kt">
                          string
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="kd">
                          var
                        </span>
                        <span class="nx">
                          t
                        </span>
                        <span class="nx">
                          T
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                          // struct

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="nx">
                          name
                        </span>
                        <span class="o">
                          :=
                        </span>
                        <span class="nx">
                          fmt
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nf">
                          Sprintf
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="s">
                          "%T"
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nx">
                          t
                        </span>
                        <span class="p">
                          )
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          if
                        </span>
                        <span class="nx">
                          name
                        </span>
                        <span class="o">
                          !=
                        </span>
                        <span class="s">
                          "&lt;nil&gt;"
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          name
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                          // interface

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          fmt
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nf">
                          Sprintf
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="s">
                          "%T"
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nb">
                          new
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ))
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="kd">
                          func
                        </span>
                        <span class="nf">
                          main
                        </span>
                        <span class="p">
                          ()
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          i
                        </span>
                        <span class="o">
                          :=
                        </span>
                        <span class="nf">
                          New
                        </span>
                        <span class="p">
                          ()
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                          // pkg1

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="kd">
                          type
                        </span>
                        <span class="nx">
                          msg
                        </span>
                        <span class="kt">
                          string
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          ProvideValue
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          msg
                        </span>
                        <span class="p">
                          ](
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nf">
                          msg
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="s">
                          "Hello World"
                        </span>
                        <span class="p">
                          ))
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                          // maybe in pkg2

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="nx">
                          fmt
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nf">
                          Println
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          Invoke
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          msg
                        </span>
                        <span class="p">
                          ](
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          ))
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                          // Output: Hello World

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                  </code>
                </pre>



 

可以看到,在 main 函数中,

  1. 通过 ProvideValue 将类型为 msg 的对象注册到 Injector 中,
  2. 使用的时候(可能在其他 pkg 中),通过 Invoke 来获取特定类型的对象。

值得一提的是,这里注册的对象可以是任何类型,包括函数、接口等。

3. 突破单例 {#3-突破单例}

如果我们使用 map[reflect.Type]reflect.Value 来存储对象,那么每个类型的对象都只能有一个实例。

这也是我们使用 map[string]any 的原因,将类型名作为默认的 key ,同时用户也可以自定义 key

下面我们增加两个函数,分别是注入自定义 name 的对象,以及通过自定义 name 获取对象。

代码如下(补全了错误处理,但仍然忽略了并发安全):

                  
                    
                      
                        
                          func
                        
                        
                          ProvideNamedValue
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          ,
                        
                        
                          name
                        
                        
                          string
                        
                        
                          ,
                        
                        
                          v
                        
                        
                          T
                        
                        
                          )
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          _
                        
                        
                          ,
                        
                        
                          ok
                        
                        
                          :=
                        
                        
                          i
                        
                        
                          .
                        
                        
                          services
                        
                        
                          [
                        
                        
                          name
                        
                        
                          ]
                        
                      
                    
                    
                      
                        
                          if
                        
                        
                          ok
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          panic
                        
                        
                          (
                        
                        
                          fmt
                        
                        
                          .
                        
                        
                          Errorf
                        
                        
                          (
                        
                        
                          "DI: service `%s` has already been declared"
                        
                        
                          ,
                        
                        
                          name
                        
                        
                          ))
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                        
                          i
                        
                        
                          .
                        
                        
                          services
                        
                        
                          [
                        
                        
                          name
                        
                        
                          ]
                        
                        
                          =
                        
                        
                          v
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                        
                          func
                        
                        
                          InvokeNamed
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          ,
                        
                        
                          name
                        
                        
                          string
                        
                        
                          )
                        
                        
                          (
                        
                        
                          t
                        
                        
                          T
                        
                        
                          ,
                        
                        
                          err
                        
                        
                          error
                        
                        
                          )
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          serviceAny
                        
                        
                          ,
                        
                        
                          ok
                        
                        
                          :=
                        
                        
                          i
                        
                        
                          .
                        
                        
                          services
                        
                        
                          [
                        
                        
                          name
                        
                        
                          ]
                        
                      
                    
                    
                      
                        
                          if
                        
                        
                          !
                        
                        
                          ok
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          return
                        
                        
                          t
                        
                        
                          ,
                        
                        
                          fmt
                        
                        
                          .
                        
                        
                          Errorf
                        
                        
                          (
                        
                        
                          "DI: could not find service `%s`"
                        
                        
                          ,
                        
                        
                          name
                        
                        
                          )
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          serviceT
                        
                        
                          ,
                        
                        
                          ok
                        
                        
                          :=
                        
                        
                          serviceAny
                        
                        
                          .(
                        
                        
                          T
                        
                        
                          )
                        
                      
                    
                    
                      
                        
                          if
                        
                        
                          !
                        
                        
                          ok
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          return
                        
                        
                          t
                        
                        
                          ,
                        
                        
                          fmt
                        
                        
                          .
                        
                        
                          Errorf
                        
                        
                          (
                        
                        
                          "DI: service `%s` is not of type `%T`"
                        
                        
                          ,
                        
                        
                          name
                        
                        
                          ,
                        
                        
                          t
                        
                        
                          )
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          return
                        
                        
                          serviceT
                        
                        
                          ,
                        
                        
                          nil
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                  
                

4. 对象调用链(懒加载) {#4-对象调用链懒加载}

上面的代码,我们能够一次注入一种类型的对象,但涉及到对象间的依赖时,我们需要按照依赖顺序,手动注入对象。

                  
                    
                      
                        
                          type
                        
                        
                          Wheel
                        
                        
                          struct
                        
                        
                          {}
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          type
                        
                        
                          Engine
                        
                        
                          struct
                        
                        
                          {}
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          type
                        
                        
                          Car
                        
                        
                          struct
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          Engine
                        
                        
                          *
                        
                        
                          Engine
                        
                      
                    
                    
                      
                        
                          Wheels
                        
                        
                          []
                        
                        
                          *
                        
                        
                          Wheel
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                  
                

以目前的实现,我们必须先注入 Engine ,再注入 Wheel ,最后注入 Car

代码大概是这样:

对象创建方(忽略错误处理):

                  
                    
                      
                        
                          engine
                        
                        
                          :=
                        
                        
                          &
                        
                        
                          Engine
                        
                        
                          {}
                        
                      
                    
                    
                      
                        
                          ProvideValue
                        
                        
                          [
                        
                        
                          *
                        
                        
                          Engine
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          ,
                        
                        
                          engine
                        
                        
                          )
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          wheel0
                        
                        
                          ,
                        
                        
                          wheel1
                        
                        
                          ,
                        
                        
                          weel2
                        
                        
                          ,
                        
                        
                          wheel3
                        
                        
                          :=
                        
                        
                          &
                        
                        
                          Wheel
                        
                        
                          {},
                        
                        
                          &
                        
                        
                          Wheel
                        
                        
                          {},
                        
                        
                          &
                        
                        
                          Wheel
                        
                        
                          {},
                        
                        
                          &
                        
                        
                          Wheel
                        
                        
                          {}
                        
                      
                    
                    
                      
                        
                          ProvideNamedValue
                        
                        
                          [
                        
                        
                          *
                        
                        
                          Wheel
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          ,
                        
                        
                          "wheel0"
                        
                        
                          ,
                        
                        
                          wheel0
                        
                        
                          )
                        
                      
                    
                    
                      
                        
                          ProvideNamedValue
                        
                        
                          [
                        
                        
                          *
                        
                        
                          Wheel
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          ,
                        
                        
                          "wheel1"
                        
                        
                          ,
                        
                        
                          wheel1
                        
                        
                          )
                        
                      
                    
                    
                      
                        
                          ProvideNamedValue
                        
                        
                          [
                        
                        
                          *
                        
                        
                          Wheel
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          ,
                        
                        
                          "wheel2"
                        
                        
                          ,
                        
                        
                          weel2
                        
                        
                          )
                        
                      
                    
                    
                      
                        
                          ProvideNamedValue
                        
                        
                          [
                        
                        
                          *
                        
                        
                          Wheel
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          ,
                        
                        
                          "wheel3"
                        
                        
                          ,
                        
                        
                          wheel3
                        
                        
                          )
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          car
                        
                        
                          :=
                        
                        
                          &
                        
                        
                          Car
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          Engine
                        
                        
                          :
                        
                        
                          Invoke
                        
                        
                          [
                        
                        
                          *
                        
                        
                          Engine
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          ),
                        
                      
                    
                    
                      
                        
                          Wheels
                        
                        
                          :
                        
                        
                          []
                        
                        
                          *
                        
                        
                          Wheel
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          // ignore error return

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="nx">
                          InvokeNamed
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          Wheel
                        </span>
                        <span class="p">
                          ](
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="s">
                          "wheel0"
                        </span>
                        <span class="p">
                          ),
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          InvokeNamed
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          Wheel
                        </span>
                        <span class="p">
                          ](
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="s">
                          "wheel1"
                        </span>
                        <span class="p">
                          ),
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          InvokeNamed
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          Wheel
                        </span>
                        <span class="p">
                          ](
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="s">
                          "wheel2"
                        </span>
                        <span class="p">
                          ),
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          InvokeNamed
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          Wheel
                        </span>
                        <span class="p">
                          ](
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="s">
                          "wheel3"
                        </span>
                        <span class="p">
                          ),
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          },
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          ProvideValue
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          Car
                        </span>
                        <span class="p">
                          ](
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nx">
                          car
                        </span>
                        <span class="p">
                          )
                        </span>
                      </span>
                    </span>
                  </code>
                </pre>



 

对象使用方:

                  
                    
                      
                        
                          c
                        
                        
                          :=
                        
                        
                          Invoke
                        
                        
                          [
                        
                        
                          Car
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          )
                        
                      
                    
                  
                

当创建 Car 时,我们必须先创建 Engine 和 Wheel,然后再创建 Car。否则在创建 Car 时,会因为无法注入 Engine 和 Wheel 而报错。

所以本质上,我们只实现了「单层」的依赖注入,只有最底层的对象,才能不用关心依赖到底是谁创建的;高层的对象,依然要注意依赖的对象是否已经创建。

而且,在实际使用过程中,Car, Engine, Wheel 这些对象,可能是在不同的 pkg 中创建的,初始化的关系是随机的(或者我们并不想关心初始化的顺序,我们只想在用的时候,能够拿到对象)。

怎么才能实现,无论依赖有多少级,都能够自动注入呢?
关键
其实问题的关键在于,在调用 ProvideVaue 时,需要传入一个确定的值,但这个值其实可以在最后要使用的时候,才去确定。 答案呼之欲出:懒加载。

代码可以这样改:

                  
                    
                      
                        
                          type
                        
                        
                          Provider
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ]
                        
                        
                          func
                        
                        
                          (
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          )
                        
                        
                          (
                        
                        
                          T
                        
                        
                          ,
                        
                        
                          error
                        
                        
                          )
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          type
                        
                        
                          lazyService
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ]
                        
                        
                          struct
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          provider
                        
                        
                          Provider
                        
                        
                          [
                        
                        
                          T
                        
                        
                          ]
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          func
                        
                        
                          Provide
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          ,
                        
                        
                          provider
                        
                        
                          Provider
                        
                        
                          [
                        
                        
                          T
                        
                        
                          ])
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          name
                        
                        
                          :=
                        
                        
                          generateServiceName
                        
                        
                          [
                        
                        
                          T
                        
                        
                          ]()
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          ProvideNamed
                        
                        
                          [
                        
                        
                          T
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          ,
                        
                        
                          name
                        
                        
                          ,
                        
                        
                          provider
                        
                        
                          )
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          func
                        
                        
                          ProvideNamed
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          ,
                        
                        
                          name
                        
                        
                          string
                        
                        
                          ,
                        
                        
                          provider
                        
                        
                          Provider
                        
                        
                          [
                        
                        
                          T
                        
                        
                          ])
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          _
                        
                        
                          ,
                        
                        
                          ok
                        
                        
                          :=
                        
                        
                          i
                        
                        
                          .
                        
                        
                          services
                        
                        
                          [
                        
                        
                          name
                        
                        
                          ]
                        
                      
                    
                    
                      
                        
                          if
                        
                        
                          ok
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          panic
                        
                        
                          (
                        
                        
                          fmt
                        
                        
                          .
                        
                        
                          Errorf
                        
                        
                          (
                        
                        
                          "DI: service `%s` has already been declared"
                        
                        
                          ,
                        
                        
                          name
                        
                        
                          ))
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          i
                        
                        
                          .
                        
                        
                          services
                        
                        
                          [
                        
                        
                          name
                        
                        
                          ]
                        
                        
                          =
                        
                        
                          lazyService
                        
                        
                          [
                        
                        
                          T
                        
                        
                          ]{
                        
                        
                          provider
                        
                        
                          :
                        
                        
                          provider
                        
                        
                          }
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                  
                

其实这有点工厂模式的味道了。

我们定义了一个 Provider 类型,由用户传入,仅仅在使用的时候,才去调用 Provider ,一个个去分析依赖,然后创建对象。

现在,我们完全可以先 Provide 一个 Car ,再去 Provide EngineWheel ,而不用关心它们的创建顺序。(只要保证,在使用前,所有的依赖都已经被 Provide 过了)

5. 重构代码 {#5-重构代码}

看到这里,读者可能已经发现了不对!

懒加载和前面的代码,services 的类型不一样! 这会导致我们在 Invoke 时,需要分情况处理。

解决方法同样很简单: 接口 !

                  
                    
                      
                        
                          type
                        
                        
                          Service
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ]
                        
                        
                          interface
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          getName
                        
                        
                          ()
                        
                        
                          string
                        
                      
                    
                    
                      
                        
                          getInstance
                        
                        
                          (
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          )
                        
                        
                          (
                        
                        
                          T
                        
                        
                          ,
                        
                        
                          error
                        
                        
                          )
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                  
                

最前面提到的,直接传入值进行 Provide 的 service,我们可以称之为「饿汉service」,实现方法可以说是一览无余:

                  
                    
                      
                        
                          type
                        
                        
                          Service
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ]
                        
                        
                          interface
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          getName
                        
                        
                          ()
                        
                        
                          string
                        
                      
                    
                    
                      
                        
                          getInstance
                        
                        
                          (
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          )
                        
                        
                          (
                        
                        
                          T
                        
                        
                          ,
                        
                        
                          error
                        
                        
                          )
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          type
                        
                        
                          eagerService
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ]
                        
                        
                          struct
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          name
                        
                        
                          string
                        
                      
                    
                    
                      
                        
                          instance
                        
                        
                          T
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          // func newEagerService ...

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="kd">
                          func
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          eagerService
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ])
                        </span>
                        <span class="nf">
                          getName
                        </span>
                        <span class="p">
                          ()
                        </span>
                        <span class="kt">
                          string
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          name
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="kd">
                          func
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          eagerService
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ])
                        </span>
                        <span class="nf">
                          getInstance
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          Injector
                        </span>
                        <span class="p">
                          )
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="kt">
                          error
                        </span>
                        <span class="p">
                          )
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          instance
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="kc">
                          nil
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                  </code>
                </pre>



 

对于懒加载,我们可以称之为「懒汉service」,实现方法如下:

                  
                    
                      
                        
                          type
                        
                        
                          lazyService
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ]
                        
                        
                          struct
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          name
                        
                        
                          string
                        
                      
                    
                    
                      
                        
                          provider
                        
                        
                          Provider
                        
                        
                          [
                        
                        
                          T
                        
                        
                          ]
                        
                      
                    
                    
                      
                        
                          instance
                        
                        
                          T
                        
                      
                    
                    
                      
                        
                          built
                        
                        
                          bool
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          // func newLazyService ...

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="kd">
                          func
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          lazyService
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ])
                        </span>
                        <span class="nf">
                          getName
                        </span>
                        <span class="p">
                          ()
                        </span>
                        <span class="kt">
                          string
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          name
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="kd">
                          func
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          lazyService
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ])
                        </span>
                        <span class="nf">
                          getInstance
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="o">
                          *
                        </span>
                        <span class="nx">
                          Injector
                        </span>
                        <span class="p">
                          )
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          t
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nx">
                          err
                        </span>
                        <span class="kt">
                          error
                        </span>
                        <span class="p">
                          )
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          if
                        </span>
                        <span class="p">
                          !
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          built
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                          // use provider to build instance

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          instance
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nx">
                          err
                        </span>
                        <span class="p">
                          =
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nf">
                          provider
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          )
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          if
                        </span>
                        <span class="nx">
                          err
                        </span>
                        <span class="o">
                          !=
                        </span>
                        <span class="kc">
                          nil
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          t
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nx">
                          err
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          s
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          built
                        </span>
                        <span class="p">
                          =
                        </span>
                        <span class="kc">
                          true
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          s
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          instance
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="kc">
                          nil
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                  </code>
                </pre>



 

代码中忽略了错误处理与并发。

现在,我们只要往 map[string]any 中存入 Service ,到时候,无论取出的是哪种实现,都可以调用 getInstance 方法,获取到实例。

6. 生命周期 {#6-生命周期}

这个 DI 库是通用的,可以注入任何对象。既然是对象,有创建,就有销毁,如何实现呢?

这就是 Go 的 interface 的优势所在了:(即鸭子类型) 对象无需显式声明实现了某个接口,只要实现了接口的方法,就是实现了该接口。 (本质上,接口就是一种约定,约定某物有某种行为。那么既然符合了约定,那就没必要使用 implement 一类的关键词来声明,非常灵活)。

回到本项目,我们只需要定义一个 shutdownableService 接口,然后在 getInstance 时,判断是否实现了该接口,如果实现了,就调用 shutdown 方法。代码大致如下:

                  
                    
                      
                        
                          type
                        
                        
                          shutdownableService
                        
                        
                          interface
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          shutdown
                        
                        
                          ()
                        
                        
                          error
                        
                      
                    
                    
                      
                        
                          }
                        
                      
                    
                    
                      
                      
                    
                    
                      
                        
                          func
                        
                        
                          Shutdown
                        
                        
                          [
                        
                        
                          T
                        
                        
                          any
                        
                        
                          ](
                        
                        
                          i
                        
                        
                          *
                        
                        
                          Injector
                        
                        
                          )
                        
                        
                          error
                        
                        
                          {
                        
                      
                    
                    
                      
                        
                          // ignore error handling

                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="c1">
                        </span>
                        <span class="nx">
                          name
                        </span>
                        <span class="o">
                          :=
                        </span>
                        <span class="nx">
                          generateServiceName
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          T
                        </span>
                        <span class="p">
                          ]()
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          serviceAny
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nx">
                          ok
                        </span>
                        <span class="o">
                          :=
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          services
                        </span>
                        <span class="p">
                          [
                        </span>
                        <span class="nx">
                          name
                        </span>
                        <span class="p">
                          ]
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          if
                        </span>
                        <span class="p">
                          !
                        </span>
                        <span class="nx">
                          ok
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          fmt
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nf">
                          Errorf
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="s">
                          "DI: could not find service `%s`"
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nx">
                          name
                        </span>
                        <span class="p">
                          )
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          service
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nx">
                          ok
                        </span>
                        <span class="o">
                          :=
                        </span>
                        <span class="nx">
                          serviceAny
                        </span>
                        <span class="p">
                          .(
                        </span>
                        <span class="nx">
                          shutdownableService
                        </span>
                        <span class="p">
                          )
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          if
                        </span>
                        <span class="nx">
                          ok
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nx">
                          err
                        </span>
                        <span class="o">
                          :=
                        </span>
                        <span class="nx">
                          service
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nf">
                          shutdown
                        </span>
                        <span class="p">
                          ()
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          if
                        </span>
                        <span class="nx">
                          err
                        </span>
                        <span class="o">
                          !=
                        </span>
                        <span class="kc">
                          nil
                        </span>
                        <span class="p">
                          {
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          return
                        </span>
                        <span class="nx">
                          err
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="nb">
                          delete
                        </span>
                        <span class="p">
                          (
                        </span>
                        <span class="nx">
                          i
                        </span>
                        <span class="p">
                          .
                        </span>
                        <span class="nx">
                          services
                        </span>
                        <span class="p">
                          ,
                        </span>
                        <span class="nx">
                          name
                        </span>
                        <span class="p">
                          )
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="k">
                          return
                        </span>
                        <span class="kc">
                          nil
                        </span>
                      </span>
                    </span>
                    <span class="line">
                      <span class="cl">
                        <span class="p">
                          }
                        </span>
                      </span>
                    </span>
                  </code>
                </pre>



 

7. 钩子 {#7-钩子}

我们在 Injector 中定义类型为 func(injector *Injector, serviceName string) 的两个钩子,分别在创建对象与销毁对象时,由 framework 调用。

要注意的是,销毁时的钩子调用顺序,类似 defer ,先进后出。

8. 并发 {#8-并发}

主要用了 sync.RWMutex ,细节详见代码。

samber/do 的不足与展望 {#samberdo-的不足与展望}

水平有限,简单谈谈我的看法。

  1. 所有的对象共用一个锁,对象多的时候,可能会影响性能。所以建议,尽量分散 Injector ,每个 Injector 管理的对象数量不要太多。
  2. codegangsta/inject 中拥有 SetParent(Injector) 功能,可以实现,先在同层级找寻找该类型依赖,找不到的时候向父级寻找(也即,能基于此实现,子层级覆盖高层级的相同类型实例)。但该库不支持,可能也是因为, samber/do 支持一个类型多个不同名实例,一定程度上可以不需要层级。

彩蛋 {#彩蛋}

  1. 作者的 GitHub ID 是 samber ,昵称是 Samuel Berthe,很可能是取了姓和名的一部分。
  2. 作者还有其他两个好玩的开源项目,命名类似: samber/lo , samber/mo
赞(0)
未经允许不得转载:工具盒子 » 源码分析——Go语言依赖注入库 samber/do