【golang】Wire依赖注入

161 阅读3分钟

依赖注入是啥

首先,程序启动时,一般都要初始化好一批对象:将对象需要的依赖都注入进去。

当然,也有运行期间初始化的,不过提前初始化性能肯定更好。


有这么个需求:程序启动,需要初始化对象A,而对象A依赖对象B

最简单的注入方式:自己手动创建对象B,然后手动注入到对象A中。

痛点:当对象非常多、依赖关系非常复杂时,手动进行依赖注入及依赖管理肯定是非常折磨、低效的。

所以,有了自动依赖注入(交给程序)。

我们的期望:

假设有100个类或结构体,在程序启动时,我们需要初始化这100个实例,我们希望有这么一个东西:把这100个类或结构体丢给它,然后它能自动完成【初始化100个实例】的工作


在Java的Spring框架中,Spring IOC容器负责进行依赖注入及依赖管理,我们只需要在类上加上@Component等注解就能将对象注册到IOC容器中,IOC容器就会帮助我们注入依赖、初始化对象

那go的世界里有没有这玩意儿?

Wire就是干这活的。

go语言中有很多这样的工具

目的、作用

是一个依赖注入 工具,旨在简化初始化代码的管理

特点

  • 通过自动生成代码的方式在编译期完成依赖注入

如何注入

具体用法看官方文档,github地址

  1. 两个角色:

    1. Provider
    2. Injector
  2. wire.go文件

  3. 自动生成代码wire_gen.go


1 创建Provider

本质是一个普通的go函数,负责创建对象(函数的返回值就是它要创建的对象),provider函数可以接收其他provider的返回值,从而形成了依赖注入。

经常一起使用的Provider可以组成Provider集合。一个Provider集合可以添加其他Provider集合。


2 创建Injector

本质是一个函数,会按依赖顺序调用Providers。

如何声明Injector?

  1. 声明一个函数。
  1. 函数body调用wire.Build,函数具体的返回值无关紧要,自动生成代码时会忽略它,只要返回值的类型正确即可

实验:能否声明多个Injector?


3 编写 wire.go


4 自动生成代码

会生成wire_gen.go文件,可以看看Injector的函数体,其实就是按依赖顺序调用Providers来创建实例

高级特性

  1. Provider的返回值类型可以是interface类型,而不用是一个具体类型。

  2. ...

参考

github.com/google/wire…

Demo

不使用wire时

不使用wire自动注入,想要创建结构体A的实例需要自己手动创建依赖、初始化结构体实例

package main

import "fmt"

type A struct {
   B
   s string
   i int
}

type B struct {
   C
   strs []string
}

type C struct {
   X, Y int
}

func newA(b *B) *A {
   return &A{B: *b, s: string("hello"), i: 100}
}

func newB(c *C) *B {
   return &B{C: *c, strs: []string{"i", "like", "apple"}}
}

func newC() *C {
   return &C{
      X: 1,
      Y: 2,
   }
}

// 不使用wire自动注入,如何创建结构体A的实例
func main() {
   // 需要自己一个个手动创建依赖,然后初始化结构体A的实例
   c := newC()
   b := newB(c)
   a := newA(b)
   fmt.Printf("a: %v %p\n", a, a)
}

使用wire时

新建wire.go文件:

//go:build wireinject
//  +build wireinject

package main

import (
   "github.com/google/wire"
)

// newA、newB、newC就是Provider,InitA就是Injector
func InitA() *A {
   wire.Build(newA, newB, newC)
   return &A{}
}

然后在当前目录下执行wire命令,就会自动生成wire_gen.go

可以看到自动生成的代码其实就是按依赖顺序调用provider来创建实例

// Code generated by Wire. DO NOT EDIT.

//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
//  +build !wireinject

package main

// Injectors from wire.go:

func InitA() *A {
   c := newC()
   b := newB(c)
   a := newA(b)
   return a
}