Google开源wire使用(依赖注入)

简介

wire 是由 google 开源的一个供 Go 语言使用的依赖注入代码生成工具。它能够根据你的代码,生成相应的依赖注入 go 代码。

依赖注入

很讨厌依赖注入这个词,new一个类并使用,硬生生给弄个新名词来衬托好高端

看看各大博客八股文怎么说明

依赖注入是一种设计模式,用于管理对象之间的依赖关系。依赖注入的核心思想是将对象的依赖关系从代码中分离出来,从而使代码更加灵活和可维护。在依赖注入中,对象不再负责创建它所依赖的对象,而是由外部容器来负责创建和管理对象之间的依赖关系。

简单来说,在A类的需要调用另外B、C、D类等,现在A类调用E类即可,E类中统一实例化了B、C、D类

常规类依赖方式

构造函数注入

构造函数注入是最常见的依赖注入方式。在构造函数中,将依赖对象作为参数传递给对象的构造函数

type UserService struct {
    userRepository *UserRepository
}
 
func NewUserService(userRepository *UserRepository) *UserService {
    return &UserService{userRepository: userRepository}
}

UserService 依赖于 UserRepository。在 NewUserService 函数中,将 UserRepository 作为参数传递给 UserService 的构造函数,并将其保存在 UserService 结构体中。这样,在创建 UserService 对象时,需要先创建 UserRepository 对象,并将其传递给 NewUserService 函数

属性注入

属性注入是将依赖对象作为对象属性进行注入的方式

type UserService struct {
    userRepository *UserRepository
}
 
func (u *UserService) SetUserRepository(userRepository *UserRepository) {
    u.userRepository = userRepository
}

UserService 依赖于 UserRepository。通过 SetUserRepository 方法将 UserRepository 注入到 UserService 中

方法注入

方法注入是将依赖对象作为方法参数进行注入的方式

type UserService struct {}
 
func (u *UserService) SaveUser(user *User, userRepository *UserRepository) error {
    return userRepository.SaveUser(user)
}

UserService 依赖于 UserRepository。通过 SaveUser 方法将 UserRepository 注入到 UserService 中

全局变量

如果你想更简单些,且依赖的资源是线程安全的,也可直接定义个全局变量来存储类,这样整个项目直接调用该变量即可 更加的简单。特别适用于一些非脚手架框架,定制化比较高的轻量级框架 如Gin等

手动依赖注入的一些好处包括:

  1. 可以更轻松地追源码,因为依赖项显式传递给函数或构造函数,例如go-zero框架的svc
  2. 管理依赖项更简单
  3. 测试依赖于手动依赖项注入的代码可能更容易,尤其是在依赖项彼此松散耦合的情况下。

三方依赖注入库

手动管理依赖其实挺好的,对一些有代码自动化洁癖、类多到爆炸的场景,可能有些不适用,Google的Wire,Facebook的Inject和Uber的Dig 都是些star数比较高的库

kratos中的wire应用

自我描述:wire是一个golang写的库,通过执行wire命令生成xx_gen.go文件帮你调用类的依赖

安装wire 一条命令就可以了

go get github.com/google/wire/cmd/wire
wire中有几个概念和常用方法

provider
各目录下包中类注册

injector
wire_gen.go文件中的注视,//+build wireinject 注释确保了这个文件在我们正常编译的时候不会被引用,而 wire . 生成的文件 wire_gen.go 会包含 //+build !wireinject 注释,正常编译的时候,不指定 tag 的情况下会引用这个文件

wire.Build()
声明要获取需要调用到哪些provider函数

将通常会一起使用的依赖组合起来
wire.NewSet()

我们通过新增一个单独模块api接口来演示依赖注入

kratos框架目录结构,几个比较重要的目录

  1. api 定义接口服务的地方
  2. cmd 项目的main方法入口文件,一切的源头
  3. configs 静态配置文件
  4. internal 项目的核心文件,相当于大多数时间编码的地方
    4.1 biz 逻辑层,logic层
    4.2 conf 配置文件读取
    4.3 server http和grpc实例的创建和配置
    4.4 service api目录中定义的接口服务handle层
第一步:api目录中定义服务

api/workbench/v1/workbench.proto

rpc ProjectCommonList(ListPortfolioRequest) returns (ListPortfolioReply){
    option (google.api.http) = {
        post: "/workbench/v1/commonproject/list"
        body: "*"
    };
}
第二步:生成对应的pb类

命令行执行

make api 
第三步:编写对应的接口handle方法

internal/service/workbench.go
CommercialService是新增的的logic

type WorkbenchService struct {
	v1.UnimplementedWorkbenchServer
	log                  *log.Helper
	userService          *biz.UserService
	fileService          *biz.FileService
	projectService       *biz.ProjectService
	commonProjectService *biz.CommonProjectService
	reviewService        *biz.ReviewService
	teamService          *biz.TeamService
	spaceService         *biz.SpaceService
	portfolioService     *biz.PortfolioService
	permService          *biz.PermService
	commercialService    *biz.CommercialService
}

func NewWorkbenchService(logger log.Logger, userService *biz.UserService, fileService *biz.FileService, projectService *biz.ProjectService,
	review *biz.ReviewService, team *biz.TeamService, space *biz.SpaceService, portfolio *biz.PortfolioService,
	perm *biz.PermService, commercial *biz.CommercialService, commomnproject *biz.CommonProjectService) *WorkbenchService {
	return &WorkbenchService{
		log:                  log.NewHelper(logger),
		userService:          userService,
		fileService:          fileService,
		projectService:       projectService,
		reviewService:        review,
		teamService:          team,
		spaceService:         space,
		portfolioService:     portfolio,
		permService:          perm,
		commercialService:    commercial,
		commonProjectService: commomnproject,
	}
}

func (w *WorkbenchService) ProjectCommonList(ctx context.Context, request *v1.ListPortfolioRequest) (*v1.ListPortfolioReply, error) {
	ret := &v1.ListPortfolioReply{
		Action:    request.Action,
		Model:     request.Model,
		RequestId: request.RequestId,
		UserIds:   make([]uint32, 0),
	}
	data, userIds, err := w.commonProjectService.ProjectCommonList(ctx, request)
	if err != nil {
		if se := new(kraErrors.Error); kraErrors.As(err, &se) {
			ret.Code = se.Code
			ret.Message = se.Message
			return ret, nil
		}
		w.log.Errorf("SetDefaultTab biz failed,err=%v", err)
		return nil, err
	}
	ret.Data = data
	ret.UserIds = userIds
	return ret, nil
}
第四步:编写对应的接口logic方法

biz文件夹中,新建commonproject.go文件
internal/biz/commonproject.go

package biz

import (
	"context"
	v1 "workbench/api/workbench/v1"

	"github.com/go-kratos/kratos/v2/log"
)

// CommonProjectService 项目相关接口
type CommonProjectService struct {
	log *log.Helper
}

func NewCommonProjectService(logger log.Logger) *CommonProjectService {
	return &CommonProjectService{
		log: log.NewHelper(logger),
	}
}

func (s *CommonProjectService) ProjectCommonList(ctx context.Context, request *v1.ListPortfolioRequest) (*v1.ListPortfolioReply_Data, []uint32, error) {
	return nil, nil, nil
}
第五步:将刚才新增的CommonProjectService.go类,注册到wire体系中

krato框架的每个目录下,都有个与文件夹同名的xxx.go文件,里面用来做wire的注册功能
internal/biz/biz.go

package biz

import (
	"workbench/internal/biz/conf"

	"github.com/google/wire"
)

// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewFileService, NewProjectService, NewReviewService, NewUserService, NewTeamService, NewSpaceService, NewPermService, NewPortfolioService, NewCommercialService, conf.NewTabService, NewBroadcastScopeService, NewCommonProjectService)
第六步:生成wire依赖注入

cmd/workbench/ 入口目录下执行命令wire

wire
第七步:运行项目
make build 
./bin/workbench

curl 0.0.0.0:8001/workbench/v1/commonproject/list

步骤回顾

kratos框架中,习惯性的将每个目录下新建一个与文件夹同名的xx.go文件,用来注册该目录下的类,例如biz目录中的biz.go中通过wire.NewSet()注册该目录下的所有类

main.go为主注册入口,该文件夹中有个wire.go文件,通过wire.Build方法加载了各个目录下的所有依赖注册文件,通过wire命令生成了一个wire_gen.go文件[此文件不可编辑,由wire自身维护]

wire.go

func wireApp(*conf.Server, *conf.Data, *conf.App, *conf.Registry, *conf.Auth, *conf.Plan, log.Logger) (*kratos.App, func(), error) {
	panic(wire.Build(server.ProviderSet, service.ProviderSet, data.ProviderSet, newApp, biz.ProviderSet))
}

main.go
在主文件中,通过调用wire_gen.go中的wireApp方法,即可实现所有的类的加载使用

app, cleanup, err := wireApp(bc.Server, bc.Data, bc.App, bc.Registry, bc.Auth, &plan, logger)

整体下来的注册流程为

  1. 每个目录下的同名文件目录名.go中通过wire.NewSet方法注册该目录下所有类,赋值给该目录下的ProviderSet变量
  2. 主文件目录下的wire.go文件,通过wire.Build方法加载各目录下注册的ProviderSet
  3. 通过wire命令生成wire_gen.go文件,主函数调用wire_gen.go文件中的wireApp即可实现类的全部依赖注入
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值