golang依赖注入wire包如何使用?

390 阅读2分钟

golang依赖注入wire包如何使用?

难题

 针对golang对象难以管理,代码重复率高,耦合度高,应该如何解决?

什么是wire?

wire是一种依赖注入工具,通过构造器进行注入(暴露NewXXX等相关方法进行注入)类似,java构造器注入。

安装

go get github.com/google/wire/cmd/wire

Provider

生成组件的普通方法。这些方法接收所需依赖作为参数,创建组件并将其返回。

Injector

`wire`自动生成的函数。函数内部会按根据依赖顺序调用相关 privoder 。

使用示例

这个 build tag 确保在常规编译时忽略 wire.go 文件(因为常规编译时不会指定 wireinject 标签)。 与之相对的是 wire_gen.go 中的 //***+build*** !wireinject 。两组对立的 build tag 保证在任意情况下, wire.go 与 wire_gen.go 只有一个文件生效, 避免了“诸多相同的方法被重复定义”的编译错误

  • wire.go
// +build wireinject
// The build tag makes sure the stub is not built in the final build.

package di

import (
   "kratos_demo/internal/dao"
   "kratos_demo/internal/service"
   "kratos_demo/internal/server/grpc"
   "kratos_demo/internal/server/http"

   "github.com/google/wire"
)

//go:generate kratos t wire
func InitApp() (*App, func(), error) {
   panic(wire.Build(dao.Provider, service.Provider, http.New, grpc.New, NewApp))
}
  • wire_gen.go

这个文件不需要编辑和额外的关注。通过wire自动生成。

// Code generated by Wire. DO NOT EDIT.

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

package di

import (
   "kratos_demo/internal/dao"
   "kratos_demo/internal/server/grpc"
   "kratos_demo/internal/server/http"
   "kratos_demo/internal/service"
)

// Injectors from wire.go:

//go:generate kratos t wire
func InitApp() (*App, func(), error) {
   redis, cleanup, err := dao.NewRedis()
   if err != nil {
      return nil, nil, err
   }
   memcache, cleanup2, err := dao.NewMC()
   if err != nil {
      cleanup()
      return nil, nil, err
   }
   db, cleanup3, err := dao.NewDB()
   if err != nil {
      cleanup2()
      cleanup()
      return nil, nil, err
   }
   daoDao, cleanup4, err := dao.New(redis, memcache, db)
   if err != nil {
      cleanup3()
      cleanup2()
      cleanup()
      return nil, nil, err
   }
   serviceService, cleanup5, err := service.New(daoDao)
   if err != nil {
      cleanup4()
      cleanup3()
      cleanup2()
      cleanup()
      return nil, nil, err
   }
   engine, err := http.New(serviceService)
   if err != nil {
      cleanup5()
      cleanup4()
      cleanup3()
      cleanup2()
      cleanup()
      return nil, nil, err
   }
   server, err := grpc.New(serviceService)
   if err != nil {
      cleanup5()
      cleanup4()
      cleanup3()
      cleanup2()
      cleanup()
      return nil, nil, err
   }
   app, cleanup6, err := NewApp(serviceService, engine, server)
   if err != nil {
      cleanup5()
      cleanup4()
      cleanup3()
      cleanup2()
      cleanup()
      return nil, nil, err
   }
   return app, func() {
      cleanup6()
      cleanup5()
      cleanup4()
      cleanup3()
      cleanup2()
      cleanup()
   }, nil
}

provider用法

var Provider = wire.NewSet(New, NewDB, NewRedis, NewMC)// 定义dao层的注入 在New 之后按照顺序添加,更具有易读性。
// New new a dao and return.
func New(r *redis.Redis, mc *memcache.Memcache, db *sql.DB) (d Dao, cf func(), err error) {
   return newDao(r, mc, db)
}

wire.Bind 用法

wire.Bind(new(HelloInf), new(*HelloClass) // 定义一个结构体和接口的对应关系 
// 类似java HelloInfService  HelloServiceImpl