欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go依賴注入DI工具wire使用詳解(golang常用庫包)

 更新時(shí)間:2022年04月13日 09:09:17   作者:九卷  
依賴注入是指程序運(yùn)行過程中,如果需要調(diào)用另一個(gè)對(duì)象協(xié)助時(shí),無須在代碼中創(chuàng)建被調(diào)用者,而是依賴于外部的注入,本文結(jié)合示例代碼給大家介紹Go依賴注入DI工具wire使用,感興趣的朋友一起看看吧

google 出品的依賴注入庫 wire:https://github.com/google/wire

什么是依賴注入

依賴注入 ,英文全名是 dependency injection,簡(jiǎn)寫為 DI。

百科解釋:

依賴注入是指程序運(yùn)行過程中,如果需要調(diào)用另一個(gè)對(duì)象協(xié)助時(shí),無須在代碼中創(chuàng)建被調(diào)用者,而是依賴于外部的注入。

在用編程語言編寫程序時(shí),比如用 java 語言,會(huì)編寫很多類,這些類之間相互調(diào)用,完成一個(gè)具體的功能。

例如,從 MySQL 獲取數(shù)據(jù),那么需要一個(gè) MySQL 操作類 。

第一次編寫mysql操作類:

class MySQL{
}

要從 mysql 獲取數(shù)據(jù),那么 mysql 數(shù)據(jù)庫的用戶名,密碼,地址等等這些配置信息,也是需要的,繼續(xù)編寫 MySQL 類:

package com.demo.mysql

class MySQL {
    getMySQLConfig() {
        port = 3306;
        username = "xxx";
        password = "xxx";
    }
    
    initMySQL(){}
   
    querySQL(){}
}

進(jìn)一步思考,上面的 MySQL 操作類程序有什么不妥的地方?

編程原則里有一個(gè)原則就是:?jiǎn)我宦氊?zé)

也就是說一個(gè)類最好只干一件事情。

根據(jù)這個(gè)原則在看看 MySQL 類,里面有獲取數(shù)據(jù)庫配置數(shù)據(jù),也有操作MySQL的方法,不是單一職責(zé)的。
那里面獲取數(shù)據(jù)庫配置數(shù)據(jù),可不可以單獨(dú)拎出來用一個(gè)類表示? 當(dāng)然可以。

因?yàn)?MySQL 配置數(shù)據(jù),多數(shù)是從文件里讀取的,上面 MySQL 類是寫死,這也是不合理的一個(gè)地方。
而配置文件的來源,可以是 yml 格式文件,也可以是 toml 格式文件,還可以是遠(yuǎn)程文件。

第二次編寫mysql操作類:

修改上面的類,增加一個(gè)獲取數(shù)據(jù)庫配置的類:

package com.demo.mysql

class MySQLConfig {
      getMySQLConfig() {
        // 從配置文件獲取 mysql 配置數(shù)據(jù)
    }
}

獲取數(shù)據(jù)的類變成:

package com.demo.mysql

class MySQL {
    initMySQL(){
     // 獲取數(shù)據(jù)庫的配置信息
     mysqlconfig = new MySQLConfig();
    }
   
    querySQL(){}
}

思考一下,上面改寫后的類有什么不妥的地方?
獲取mysql的配置信息,是不是要在 MySQL 類里 new一下, 實(shí)例化一下,如果不在同一個(gè)包下,還要把配置類引入進(jìn)來在才能實(shí)例化。這里能不能優(yōu)化下,當(dāng)然可以。

直接把數(shù)據(jù)庫配置類注入到 MySQL 操作類里。這就是依賴注入。

  • 依賴是什么?注入又是什么?

mysql 操作類依賴誰?依賴數(shù)據(jù)庫配置類。
注入什么?把數(shù)據(jù)庫配置類注入到 mysql 操作類里。
注入是一個(gè)動(dòng)作,把一個(gè)類注入到另外一個(gè)類。
依賴是一種關(guān)系,類關(guān)系,一個(gè)類要完全發(fā)揮作用,需要依賴另外一個(gè)類。

要完成數(shù)據(jù)操作,mysql操作類是需要依賴數(shù)據(jù)庫配置類的,把數(shù)據(jù)庫配置類注入到mysql操作類里,就可以完成操作類功能。

第三次編寫mysql操作類:

偽代碼示例:

package com.demo.mysql

class MySQL {
    private MySQLConfig config
    MySQL(MySQLConfig mysqlconfig) { // 數(shù)據(jù)庫配置類這里注入到mysql操作類里
        config = mysqlconfig
    }
    initMySQL(){
    
    }
   
    querySQL(){}
}

把數(shù)據(jù)庫配置類注入到mysql操作類里。

寫 java 的人都知道 java 框架里有一個(gè) spring 全家桶,spring 框架包核心有2個(gè),其中有一個(gè)核心就是 IoC,另一個(gè)是 aop。

IoC 的全稱:Inversion of Control,控制反轉(zhuǎn)。

這個(gè)控制反轉(zhuǎn)也是面向?qū)ο缶幊淘瓌t之一。

但是這個(gè)控制反轉(zhuǎn)比較難理解,如果結(jié)合上面的 DI 來理解,就比較容易理解點(diǎn)。
可以把 DI 看作是 IoC 編程原則的一個(gè)具體實(shí)現(xiàn)。

依賴注入還可以從另外的軟件設(shè)計(jì)思想來理解:

  1. 分離關(guān)注點(diǎn)
  2. 高內(nèi)聚,低耦合

對(duì)數(shù)據(jù)庫 mysql 的操作和 mysql 的配置信息,這個(gè) 2 個(gè)是可以相互獨(dú)立,相分離的。

何時(shí)使用依賴注入

當(dāng)你的項(xiàng)目規(guī)模不大,文件不是很多,一個(gè)文件調(diào)用只需要傳入少量依賴對(duì)象時(shí),這時(shí)使用依賴注入就會(huì)使程序變得繁瑣。

當(dāng)規(guī)模變大,單個(gè)對(duì)象使用需要調(diào)用多個(gè)依賴對(duì)象時(shí),而這些依賴又有自己依賴對(duì)象,這時(shí)對(duì)象創(chuàng)建變得繁瑣,那么這時(shí)候依賴注入就可以出場(chǎng)了。

wire 概念說明

wire 簡(jiǎn)介

wire 是由 google 開源的一個(gè)用 Go 語言實(shí)現(xiàn)的依賴注入代碼生成工具。它能夠根據(jù)你寫的代碼生成相應(yīng)的依賴注入 Go 代碼。

與其他依賴注入工具不同,比如 uber 的 dig 和 facebook 的 inject,這 2 個(gè)工具都是使用反射實(shí)現(xiàn)的依賴注入,而且是運(yùn)行時(shí)注入(runtime dependency injection)。

wire 是編譯代碼時(shí)生成代碼的依賴注入,是編譯期間注入依賴代碼(compile-time dependency injection)。而且代碼生成期間,如果依賴注入有問題,生成依賴代碼時(shí)就會(huì)出錯(cuò),就可以報(bào)出問題來,而不必等到代碼運(yùn)行時(shí)才暴露出問題。

provider 和 injector

首先,需要理解 wire 的 2 個(gè)核心概念:provider 和 injector。

從上面 java 模擬依賴注入的例子中,可以簡(jiǎn)化出依賴注入的步驟:

第一:需要 New 出一個(gè)類實(shí)例
第二:把這個(gè) New 出來的類實(shí)例通過構(gòu)造函數(shù)或者其他方式“注入”到需要使用它的類中
第三:在類中使用這個(gè) New 出來的實(shí)例

從上面步驟來理解 wire 的 2 個(gè)核心概念 provider 和 injector。

provider 就相當(dāng)于上面 New 出來的類實(shí)例。
injector 就相當(dāng)于“注入”動(dòng)作前,把所需依賴函數(shù)進(jìn)行聚合,根據(jù)這個(gè)聚合的函數(shù)生成依賴關(guān)系。

provider:提供一個(gè)對(duì)象。
injector:負(fù)責(zé)根據(jù)對(duì)象依賴關(guān)系,生成新程序。

provider

provider 是一個(gè)普通的 Go 函數(shù) ,可以理解為是一個(gè)對(duì)象的構(gòu)造函數(shù)。為下面生成 injector 函數(shù)提供”構(gòu)件“。

看下面例子,來自 go blog。

這篇 blog 是 2018.10.9 發(fā)表,可能一些信息有點(diǎn)老,再參考 github guide ,這篇 guide 最后更新于 2021.1.26。

下面的 NewUserStore() 函數(shù)可以看作是一個(gè) provider。這個(gè)函數(shù)需要傳入 *Config 和 *mysql.DB 2 個(gè)參數(shù)。

// NewUserStore 是一個(gè) provider for *UserStore,*UserStore 依賴 *Config,*mysql.DB
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {... ...}

// NewDefaultConfig 是一個(gè) provider for *Config,沒有任何依賴
func NewDefaultConfig() *Config {...}

// NewDB 是 *mysql.DB 的一個(gè) provider ,依賴于數(shù)據(jù)庫連接信息 *ConnectionInfo
func NewDB(info *ConnectionInfo) (*mysql.DB, error){...}

provider 可以組合成一組 provider set。對(duì)于經(jīng)常在一起使用的 providers 來說,這個(gè)非常有用。使用 wire.NewSet 方法可以把他們組合在一起,

var SuperSet = wire.NewSet(NewUserStore, NewDefaultConfig)

你也可以把其他的 provider sets 加入一個(gè) provider set,

import (
    “example.com/some/other/pkg”
)

// ... ...
var MegaSet = wire.NewSet(SuperSet, pkg.OtherSet)

wire.NewSet() 函數(shù):

這個(gè)函數(shù)可以把相關(guān)的 provider 組合在一起然后使用。當(dāng)然也可以單獨(dú)使用,如 var Provider = wire.NewSet(NewDB)。

這個(gè) NewSet 函數(shù)的返回值也可以作為其他 NewSet 函數(shù)的參數(shù)使用,比如上面的 SuperSet 作為參數(shù)使用。

injector

我們編寫程序把這些 providers 組合起來(比如下面例子 initUserStore() 函數(shù)),wire 里的 wire 命令會(huì)按照依賴順序調(diào)用 providers 生成更加完整的函數(shù),這個(gè)就是 injector。

首先,編寫生成 injector 的簽名函數(shù),然后用 wire 命令生成相應(yīng)的函數(shù)。

例子如下:

// +build wireinject

func initUserStore(info *ConnectionInfo) (*UserStore, error) {
    wire.Build(SuperSet, NewDB) // 聲明獲取 UserStore 需要調(diào)用哪些 provider 函數(shù)
    return nil, nil
}

然后用 wire 命令把上面的 initUserStore 函數(shù)生成 injector 函數(shù),生成的函數(shù)對(duì)應(yīng)文件名 wire_gen.go。

wire 命令:

You can generate the injector by invoking Wire in the package directory。

直接在生成 injector 函數(shù)的包下,使用 wire 命令,就可以生成 injector 代碼。

wire.Build() 函數(shù):

它的參數(shù)可以是 wire.NewSet() 組織的一個(gè)或多個(gè) provider,也可以直接使用 provider。

wire 使用

wire 結(jié)構(gòu)體和方法列表

func Build(...interface{}) string
type Binding
	func Bind(iface, to interface{}) Binding
type ProvidedValue
	func InterfaceValue(typ interface{}, x interface{}) ProvidedValue
	func Value(interface{}) ProvidedValue
type ProviderSet
	func NewSet(...interface{}) ProviderSet
type StructFields
	func FieldsOf(structType interface{}, fieldNames ...string) StructFields
type StructProvider
	func Struct(structType interface{}, fieldNames ...string) StructProvider

更詳細(xì)說明可以看這里 func index - pkg.go.dev。

wire 安裝

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

快速開始

例子1

先新建一個(gè) basics 的文件夾,然后在 basics 里使用 go mod init basics,新建一個(gè) go.mod,在 go.mod 里引入 wire:require github.com/google/wire v0.5.0。

整個(gè)文件夾目錄結(jié)構(gòu):

定義 providers

在 basics 文件夾下新建 basics.go 文件,寫入如下代碼:

package main

import (
	"context"
	"errors"
)

type Student struct {
	ClassNo int
}

// NewStudent 就是一個(gè) provider,返回一個(gè) Student
func NewStudent() Student {
	return Student{ClassNo: 10}
}

type Class struct {
	ClassNo int
}

// NewClass 就是一個(gè) provider,返回一個(gè) Class
func NewClass(stu Student) Class {
	return Class{ClassNo: stu.ClassNo}
}

type School struct {
	ClassNo int
}

// NewSchool 是一個(gè) provider,返回一個(gè) School
// 與上面 provider 不同的是,它還返回了一個(gè)錯(cuò)誤信息
func NewSchool(ctx context.Context, class Class) (School, error) {
	if class.ClassNo == 0 {
		return School{}, errors.New("cannot provider school when class is 0")
	}
	return School{ClassNo: class.ClassNo}, nil
}

定義 injector

新建文件 wire.go,代碼如下:

// +build wireinject

package main

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

var SuperSet = wire.NewSet(NewStudent, NewClass, NewSchool)

func initSchool() (School, error) {
	wire.Build(SuperSet)
	return School{}, nil
}

// +build wireinject

這一行代碼一定要在包最上面聲明,表明這是一個(gè)準(zhǔn)備被編譯的 injector

用 wire 命令生成 injector 函數(shù)代碼

wire 命令生成 injector 代碼,在 basics 目錄下執(zhí)行 wire 命令:

$ wire
wire: D:\work\mygo\go-practice2\di\wire\basics\wire.go:9:1: inject initSchool: no provider found for context.Context needed by basics.School in provider set "SuperSet" (D:\work\mygo\go-practice2\di\wire\basics\wire.go:7:16)

wire: basics: generate failed
wire: at least one generate failure

報(bào)錯(cuò)了,看看顯示出的錯(cuò)誤信息,最主要是這一行信息:

inject initSchool: no provider found for context.Context needed by basics.School in provider set "SuperSet"

來看一看 initSchool 函數(shù),果然沒有給它提供 context.Context 。我們來修改函數(shù),引入 context 包,然后給 initSchool 函數(shù)增加參數(shù) context.Context

func initSchool(ctx context.Context) (School, error) {
	wire.Build(SuperSet)
	return School{}, nil
}

再來用命令 wire 編譯:

$ wire
wire: basics: wrote D:\work\mygo\go-practice2\di\wire\basics\wire_gen.go

生成的 injector 代碼,wire_gen.go 文件,

// Code generated by Wire. DO NOT EDIT.

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

package main

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

// Injectors from wire.go:

func initSchool(ctx context.Context) (School, error) {
	student := NewStudent()
	class := NewClass(student)
	school, err := NewSchool(ctx, class)
	if err != nil {
		return School{}, err
	}
	return school, nil
}

// wire.go:

var SuperSet = wire.NewSet(NewStudent, NewClass, NewSchool)

小結(jié)

wire 使用的步驟:

先編寫 provider。再編寫 injector:把相關(guān) provider 組織在一起,成為一個(gè) ProviderSet。最后用 wire 命令編譯:wire 會(huì)根據(jù) provider 之間相關(guān)依賴生成代碼。

wire.NewSet 函數(shù):

它可以把 provider 集合起來。作用1分類:可以把一組相關(guān)的 provider 寫在一起組成 ProviderSet。作用1延伸第2個(gè)作用,避免 provider 過多難于管理。

wite.Build 函數(shù):

func Build(...interface{}) string

它的參數(shù)是 provider 不定長(zhǎng)列表。 把所有相關(guān)的 provider 組織在一起然后生成 injector 函數(shù)代碼。它是生成 injector 函數(shù)的模板函數(shù)。

綁定接口

上面例子1綁定的是結(jié)構(gòu)體和構(gòu)造函數(shù)。如果有接口 interface 參與呢,那怎么辦?比如下面的代碼,

type Fooer interface {
    Hello()
}

type Foo struct{}

func (f Foo)Hello() {
    fmt.Println("hello")
}

func Bar struct{}

func NewBar() Bar {
    return Bar{}
}

有接口 Fooer,這個(gè)怎么綁定呢?這時(shí)候就可以用 [wire.Bind](wire/wire.go at v0.5.0 · google/wire · GitHub) 函數(shù),

var bind = wire.Bind(new(Fooer), new(Foo))
var set = wire.NewSet(bind, NewBar)

// or
var set = wire.NewSet(wire.Bind(new(Fooer), new(Foo)), NewBar)

struct prividers

struct 也可以直接當(dāng)作一個(gè) provider 使用。如果結(jié)構(gòu)體的 provider 僅僅是用作字段賦值,那么可以使用函數(shù) wire.Struct 來賦值。

type Foo int
type Bar int

func NewFoo() Foo {/* ... */}
func NewBar() Bar {/* ... */}

type FooBar struct {
    MyFoo Foo
    MyBar Bar
}

var set = wire.NewSet(
	NewFoo,
    NewBar,
    wire.Struct(new(FooBar), "MyFoo", "MyBar"),
)

更多信息請(qǐng)參考struct providers guide

Provider Set

上面例子1中就用到 provider set,把

相關(guān)的 provider 組織在一起。使用函數(shù) wire.NewSet 就可以做到。

更多例子請(qǐng)查看官方文檔:

https://github.com/google/wire

參考

https://github.com/google/wire

https://github.com/google/wire/blob/main/docs/guide.md

https://go.dev/blog/wire

wire func index - pkg

作者: 九卷
出處:https://www.cnblogs.com/jiujuan/p/16136633.html
版權(quán):本文采用「署名-非商業(yè)性使用-相同方式共享 4.0 國(guó)際」知識(shí)共享許可協(xié)議進(jìn)行許可。

到此這篇關(guān)于Go依賴注入DI工具wire使用詳解(golang常用庫包)的文章就介紹到這了,更多相關(guān)Go依賴注入wire內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言并發(fā)編程之控制并發(fā)數(shù)量實(shí)現(xiàn)實(shí)例

    Go語言并發(fā)編程之控制并發(fā)數(shù)量實(shí)現(xiàn)實(shí)例

    這篇文章主要為大家介紹了Go語言并發(fā)編程之控制并發(fā)數(shù)量實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • GoFrame?gtree樹形結(jié)構(gòu)的使用技巧示例

    GoFrame?gtree樹形結(jié)構(gòu)的使用技巧示例

    這篇文章主要為大家介紹了GoFrame?gtree樹形結(jié)構(gòu)的使用技巧示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go語言題解LeetCode705設(shè)計(jì)哈希集合

    Go語言題解LeetCode705設(shè)計(jì)哈希集合

    這篇文章主要為大家介紹了Go語言題解LeetCode705設(shè)計(jì)哈希集合,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 解析Go語言編程中的struct結(jié)構(gòu)

    解析Go語言編程中的struct結(jié)構(gòu)

    這篇文章主要介紹了Go語言編程中的struct結(jié)構(gòu),是Go語言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • 這些關(guān)于Go中interface{}的注意事項(xiàng)你都了解嗎

    這些關(guān)于Go中interface{}的注意事項(xiàng)你都了解嗎

    這篇文章主要為大家詳細(xì)介紹了學(xué)習(xí)Go語言時(shí)需要了解的interface{}注意事項(xiàng),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2023-03-03
  • gin框架中使用JWT的定義需求及解析

    gin框架中使用JWT的定義需求及解析

    這篇文章主要為介紹了gin框架中使用JWT的定義需求及解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • go mod tidy拉取依賴包bug問題及解決

    go mod tidy拉取依賴包bug問題及解決

    這篇文章主要介紹了go mod tidy拉取依賴包bug問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • golang import自定義包方式

    golang import自定義包方式

    這篇文章主要介紹了golang import自定義包方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go語言中的字符串拼接方法詳情

    Go語言中的字符串拼接方法詳情

    本文介紹Go語言中的string類型、strings包和bytes.Buffer類型,介紹幾種字符串拼接方法的相關(guān)資料,需要的朋友可以參考一下,希望對(duì)你有所幫助
    2021-10-10
  • goLand Delve版本太老的問題及解決

    goLand Delve版本太老的問題及解決

    這篇文章主要介紹了goLand Delve版本太老的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09

最新評(píng)論