<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    依賴注入工具代碼生成器 wire

    VSole2022-08-04 11:06:38

    簡介 

    wire是一個代碼生成工具,它通過自動生成代碼的方式完成依賴注入。

    應用場景

    wire作為依賴注入的代碼生成工具,非常適合復雜對象的創建。而在大型項目中,擁有一個合適的依賴注入的框架將使得項目的開發與維護十分便捷。

    Wire核心概念

    wire 中最核心的兩個概念就是Injector和Provider。

    Provider : 生成組件的普通方法。這些方法接收所需依賴作為參數,創建組件并將其返回

    Injector : 代表了我們最終要生成的構建函數的函數簽名,返回值代表了構建的目標,在最后生成的代碼中,此函數簽名會完整的保留下來。

    安裝

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

     代碼生成

    命令行在指定目錄下執行 wire命令即可。

    示例學習

    https://github.com/google/wire/tree/main/internal/wire/testdata/

     成員介紹

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

    基礎代碼

    main.go

    package main
    type Leaf struct {
        Name string
    }
    type Branch struct{
        L Leaf
    }
    type Root struct {
        B Branch
    }
    func NewLeaf(name string) Leaf {return Leaf{Name:name}}
    func NewBranch(l Leaf) Branch {return Branch{L:l}}
    func NewRoot(b Branch) Root {return Root{B:b}}
    

    wire.go

    // +build wireinject
    // The build tag makes sure the stub is not built in the final build.
    package main
    import (
        "github.com/google/wire"
    )
    func InitRoot(name string) Root {
        wire.Build(NewLeaf,NewBranch,NewRoot)
        return Root{}
    }
    

    wire_gen.go

    // Code generated by Wire. DO NOT EDIT.
    //go:generate wire
    //+build !wireinject
    package main
    // Injectors from wire.go:
    func InitRoot(name string) Root {
     leaf := NewLeaf(name)
     branch := NewBranch(leaf)
     root := NewRoot(branch)
     return root
    }
    

    這里我們可以看到代碼的生成是根據wire.Build參數的輸入與輸出類型來決定的。

    wire.Build的參數是Provider的不定長列表。

    wire包成員的作用

    wire的成員每一個都是為了Provider服務的,他們各自有適用的場景。

    NewSet 

    NewSet的作用是為了防止Provider過多導致混亂,它把一組業務相關的Provider放在一起組織成ProviderSet。

    wire.go可以寫成

    var NewBranchSet = wire.NewSet(NewLeaf,NewBranch)
    func InitRoot(name string) Root {
        wire.Build(NewBranchSet,NewRoot)
        return Root{}
    }
    

    值得注意的事,NewSet可以寫在原結構體所在的文件中,以方便切換和維護。

    Bind

    Bind函數的作用是為了讓接口類型參與wire的構建過程。wire的構建依靠的是參數的類型來組織代碼,所以接口類型天然是不支持的。Bind函數通過將接口類型和實現類型綁定,來達到依賴注入的目的。

    type Fooer interface{
        HelloWorld() 
    }
    type Foo struct{}
    func (f Foo)HelloWorld(){}
    var bind = wire.Bind(new(Fooer),new(Foo))
    

    示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/BindInjectorArgPointer

    這樣將bind傳入NewSet或Build中就可以將Fooer接口和Foo類型綁定。

    這里需要特別注意,如果是*Foo實現了Fooer接口,需要將最后的new(Foo)改成new(*Foo)

    Struct

    Struct函數用于簡化結構體的Provider,當結構體的Provider僅僅是字段賦值時可以使用這個函數。

    //當Leaf中成員變量很多時,或者只需要部分初始化時,構造函數會變得很復雜
    func NewLeaf(name string) Leaf {return Leaf{Name:name}}
    //等價寫法
    //部分字段初始化
    wire.Struct(new(Leaf),"Name")
    //全字段初始化
    wire.Struct(new(Leaf),"*")
    

    這里的NewLeaf函數可以被下面的部分字段初始化函數替代。

    Struct函數可以作為Provider出現在Build或NewSet的參數中。

    FieldsOf

    FieldsOf函數可以將結構體中的對應字段作為Provider,供wire使用。在上面的代碼基礎上,我們做如下的等價

    //獲得Leaf中Name字段的Provider
    func NewName(l Leaf) string {return l.Name}
    //等價寫法
    //FieldsOf的方式獲得結構體內的字段
    wire.FieldsOf(new(Leaf),"Name")
    

    示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/FieldsOfStruct

    這里的代碼是等價的,但是卻不能和上面的代碼共存,原因稍后會解釋。

    Value

    Value函數為基本類型的屬性綁定具體值,在基于需求的基礎上簡化代碼。

    func NewLeaf()Leaf{
        return Leaf{
            Name:"leaf",
        }
    }
    //等價寫法
    wire.Value(Leaf{Name:"leaf"})
    

    以上兩個函數在作為Provider上也是等價的,可以出現在Build或NewSet中。

    InterfaceValue

    InterfaceValue作用與Value函數類似,只是InterfaceValue函數是為接口類型綁定具體值。

    wire.InterfaceValue(new(io.Reader),os.Stdin)
    

    比較少用到,這里就不細講了。

    返回值的特殊情況

    返回值 error

    wire是支持返回對象的同時攜帶error的。對于error類型的返回值,wire也能很好的處理。

    //main.go
    func NewLeaf(name string) (Leaf, error) { return Leaf{Name: name}, nil }
    //wire.go
    func InitRoot(name string) (Root, error) {
        ...
    }
    //wire_gen.go
    func InitRoot(name string) (Root, error) {
        leaf, err := NewLeaf(name)
        if err != nil {
            return Root{}, err 
        }   
        branch := NewBranch(leaf)
        root := NewRoot(branch)
        return root, nil 
    }
    

    示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/ReturnError

    可以看到當Provider中出現error的返回值時,Injector函數的返回值中也必須攜帶error的返回值

    清理函數CleanUp

    清理通常出現在有文件對象,socket對象參與的構建函數中,無論是出錯后的資源關閉,還是作為正常獲得對象后的析構函數都是有必要的。

    清理函數通常作為第二返回值,參數類型為func(),即為無參數無返回值的函數對象。跟error一樣,當Provider中的任何一個擁有清理函數,Injector的函數簽名返回值中也必須包含該函數類型。

    //main.go
    func NewLeaf(name string) (Leaf, func()) {
        r := Leaf{Name: name}
        return r, func() { r.Name = "" }
    }
    func NewBranch(l Leaf) (Branch, func()) { return Branch{L: l}, func() {} }
    //wire.go
    func InitRoot(name string) (Root, func()) {...}
    //wire_gen.go
    func InitRoot(name string) (Root, func()) {
        leaf, cleanup := NewLeaf(name)
        branch, cleanup2 := NewBranch(leaf)
        root := NewRoot(branch)
        return root, func() {
            cleanup2()
            cleanup()
        }   
    }
    

    示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/Cleanup

    就這樣名為cleanup的清理函數就隨著InitRoot返回了。當有多個Provider有cleanup的時候,wire會自動把cleanup加入到最后的返回函數中。

    常見問題

    類型重復

    基礎類型

    基礎類型是構建結構體的基礎,其作為參數創建結構體是十分常見的,參數類型重復更是不可避免的。wire通過Go語言語法中的"type A B"的方法來解決詞類問題。

    //wire.go
    type Account string
    func InitRoot(name string, account Account) (Root, func()) {...}
    出現在wire.go中的"type A B" 會自動復制到wire_gen.go中
    

    示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/InjectInput

    個人觀點 wire著眼于復雜對象的構建,因此基礎類型的屬性賦值推薦使用結構體本身的Set操作完成。

    對象類型重復

    每一個Provider都是一個組件的生成方法,如果有兩個Provider生成同一類組件,那么在構建過程中就會產生沖突,這里需要特別注意,保證組件的類型唯一性。

    循環構建

    循環構建指的是多個Provider相互提供參數和返回值形成一個閉環。當wire檢查構建的流程含有閉環構建的時候,就會報錯。

    type Root struct{
        B Branch
    }
    type Branch struct {
        L Leaf
    }
    type Leaf struct {
        R Root
    }
    func NewLeaf(r Root) Leaf {return Leaf{R:r}}
    func NewBranch(l Leaf) Branch {return Branch{L:l}}
    func NewRoot(b Branch) Root {return Root{B:b}}
    ...
    wire.Build(NewLeaf,NewRranch,NewRoot) //錯誤 cycle for XXX
    ...
    

    示例地址:https://github.com/google/wire/tree/main/internal/wire/testdata/Cycle

    小結

    wire是一個強大的工具,它在不運行Go程序的基礎上,借助于特定文件("http://+build wireinject")的解析,自動生成對象的構造函數代碼。

    Go語言工程化的過程中,涉及到諸多對象的包級別歸類,wire可以很好的協助我們完成復雜對象的構建過程。

    函數依賴返回值
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    okhttp因其穩定、開源、簡單易用被眾多的app開發人員所使用。okhttp中的execute和enqueue分別為發出同步和異步請求,通過對okhttp發起請求的整個流程進行簡單分析就可以快速得到合適的hook點完成對app網絡請求的抓包和溯源。
    angr是有名的符號執行工具。眾所周知,符號執行最大的問題就是狀態爆炸。因此,angr中也內置了很多緩解符號執行的策略。本文主要介紹angr里的一些內置機制是如何緩解狀態爆炸的。
    如果要反匯編成smali,起碼要知道這條smali對應的字節碼一共幾個字節。在確定一條指令占幾個字節后,還要知道這幾個字節中,誰是操作碼,誰是操作數。有了前兩步鋪墊,最終我們可以解讀一條完整的smali的含義。
    應用場景wire作為依賴注入的代碼生成工具,非常適合復雜對象的創建。而在大型項目中,擁有一個合適的依賴注入的框架將使得項目的開發與維護十分便捷。中最核心的兩個概念就是Injector和Provider。這些方法接收所需依賴作為參數,創建組件并將其返回Injector?代碼生成命令行在指定目錄下執行?
    linux跟蹤技術之ebpf
    2022-12-30 10:51:15
    eBPF是一項革命性的技術,起源于 Linux 內核,可以在操作系統內核等特權上下文中運行沙盒程序。它可以安全有效地擴展內核的功能,而無需更改內核源代碼或加載內核模塊。比如,使用ebpf可以追蹤任何內核導出函數的參數,返回值,以實現kernel hook 的效果;通過ebpf,還可以在網絡封包到達內核協議棧之前就進行處理,這可以實現流量控制,甚至隱蔽通信。
    高達40%的npm包依賴的代碼至少包含一個公開漏洞,因此如何解決 Node.js 應用的安全性檢測是一個十分重要的問題。
    針對現有的靜態代碼分析工具有較高的誤報率與漏報率,提出一種基于切片依賴圖(Slice Dependency Graph,SDG)的自動化漏洞檢測方法,將程序源代碼解析為包含數據依賴和控制依賴信息的切片依賴圖,然后使用圖神經網絡對切片依賴圖的結構進行表征學習,最后使用訓練的神經網絡模型預測待測程序源代碼中的漏洞。在 5 類常見缺陷分類(Common Weakness Enumeration,CWE)
    源碼分析1、LLVM編譯器簡介LLVM 命名最早源自于底層虛擬機的縮寫,由于命名帶來的混亂,LLVM就是該項目的全稱。LLVM 核心庫提供了與編譯器相關的支持,可以作為多種語言編譯器的后臺來使用。自那時以來,已經成長為LLVM的主干項目,由不同的子項目組成,其中許多是正在生產中使用的各種 商業和開源的項目,以及被廣泛用于學術研究。
    STATEMENT聲明由于傳播、利用此文所提供的信息而造成的任何直接或者間接的后果及損失,均由使用者本人負責,雷神眾測及文章作者不為此承擔任何責任。雷神眾測擁有對此文章的修改和解釋權。
    它指的是一個有用的工具庫,幫助處理和操作XML格式的數據。ROME庫允許我們把XML數據轉換成Java中的對象,這樣我們可以更方便地在程序中操作數據。另外,它也支持將Java對象轉換成XML數據,這樣我們就可以把數據保存成XML文件或者發送給其他系統。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类