日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
創(chuàng)新互聯(lián)GO教程:Go語言inject庫:依賴注入

在介紹 inject 之前我們先來簡單介紹一下“依賴注入”和“控制反轉(zhuǎn)”這兩個(gè)概念。

創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供商州網(wǎng)站建設(shè)、商州做網(wǎng)站、商州網(wǎng)站設(shè)計(jì)、商州網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、商州企業(yè)網(wǎng)站模板建站服務(wù),10余年商州做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

正常情況下,對(duì)函數(shù)或方法的調(diào)用是我們的主動(dòng)直接行為,在調(diào)用某個(gè)函數(shù)之前我們需要清楚地知道被調(diào)函數(shù)的名稱是什么,參數(shù)有哪些類型等等。

所謂的控制反轉(zhuǎn)就是將這種主動(dòng)行為變成間接的行為,我們不用直接調(diào)用函數(shù)或?qū)ο?,而是借助框架代碼進(jìn)行間接的調(diào)用和初始化,這種行為稱作“控制反轉(zhuǎn)”,庫和框架能很好的解釋控制反轉(zhuǎn)的概念。

依賴注入是實(shí)現(xiàn)控制反轉(zhuǎn)的一種方法,如果說控制反轉(zhuǎn)是一種設(shè)計(jì)思想,那么依賴注入就是這種思想的一種實(shí)現(xiàn),通過注入?yún)?shù)或?qū)嵗姆绞綄?shí)現(xiàn)控制反轉(zhuǎn)。如果沒有特殊說明,我們可以認(rèn)為依賴注入和控制反轉(zhuǎn)是一個(gè)東西。

控制反轉(zhuǎn)的價(jià)值在于解耦,有了控制反轉(zhuǎn)就不需要將代碼寫死,可以讓控制反轉(zhuǎn)的的框架代碼讀取配置,動(dòng)態(tài)的構(gòu)建對(duì)象,這一點(diǎn)在 Java 的 Spring 框架中體現(xiàn)的尤為突出。

inject 實(shí)踐

inject 是依賴注入的Go語言實(shí)現(xiàn),它能在運(yùn)行時(shí)注入?yún)?shù),調(diào)用方法,是 Martini 框架(Go語言中著名的 Web 框架)的基礎(chǔ)核心。

在介紹具體實(shí)現(xiàn)之前,先來想一個(gè)問題,如何通過一個(gè)字符串類型的函數(shù)名來調(diào)用函數(shù)?Go語言沒有 Java 中的 Class.forName 方法可以通過類名直接構(gòu)造對(duì)象,所以這種方法是行不通的,能想到的方法就是使用 map 實(shí)現(xiàn)一個(gè)字符串到函數(shù)的映射,示例代碼如下:

func fl() {
    println ("fl")
}
func f2 () {
    println ("f2")
}
funcs := make(map[string] func ())
funcs ["fl"] = fl
funcs ["f2"] = fl
funcs ["fl"]()
funcs ["f2"]()

但是這有個(gè)缺陷,就是 map 的 Value 類型被寫成 func(),不同參數(shù)和返回值的類型的函數(shù)并不能通用。將 map 的 Value 定義為 interface{} 空接口類型即可以解決該問題,但需要借助類型斷言或反射來實(shí)現(xiàn),通過類型斷言實(shí)現(xiàn)等于又繞回去了,反射是一種可行的辦法。

inject 包借助反射實(shí)現(xiàn)函數(shù)的注入調(diào)用,下面通過一個(gè)示例來看一下。

package main

import (
    "fmt"
    "github.com/codegangsta/inject"
)

type S1 interface{}
type S2 interface{}

func Format(name string, company S1, level S2, age int) {
    fmt.Printf("name = %s, company=%s, level=%s, age = %d!\n", name, company, level, age)
}
func main() {
    //控制實(shí)例的創(chuàng)建
    inj := inject.New()
    //實(shí)參注入
    inj.Map("tom")
    inj.MapTo("tencent", (*S1)(nil))
    inj.MapTo("T4", (*S2)(nil))
    inj.Map(23)
    //函數(shù)反轉(zhuǎn)調(diào)用
    inj.Invoke(Format)
}

運(yùn)行結(jié)果如下:

name = tom, company=tencent, level=T4, age = 23!

可見 inject 提供了一種注入?yún)?shù)調(diào)用函數(shù)的通用功能,inject.New() 相當(dāng)于創(chuàng)建了一個(gè)控制實(shí)例,由其來實(shí)現(xiàn)對(duì)函數(shù)的注入調(diào)用。inject 包不但提供了對(duì)函數(shù)的注入,還實(shí)現(xiàn)了對(duì) struct 類型的注入,示例代碼如下所示:

package main

import (
    "fmt"
    "github.com/codegangsta/inject"
)

type S1 interface{}
type S2 interface{}
type Staff struct {
    Name    string `inject`
    Company S1     `inject`
    Level   S2     `inject`
    Age     int    `inject`
}

func main() {
    //創(chuàng)建被注入實(shí)例
    s := Staff{}
    //控制實(shí)例的創(chuàng)建
    inj := inject.New()
    //初始化注入值
    inj.Map("tom")
    inj.MapTo("tencent", (*S1)(nil))
    inj.MapTo("T4", (*S2)(nil))
    inj.Map(23)
    //實(shí)現(xiàn)對(duì) struct 注入
    inj.Apply(&s)
    //打印結(jié)果
    fmt.Printf("s = %v\n", s)
}

運(yùn)行結(jié)果如下:

s = {tom tencent T4 23}

可以看到 inject 提供了一種對(duì)結(jié)構(gòu)類型的通用注入方法。至此,我們僅僅從宏觀層面了解 iniect 能做什么,下面從源碼實(shí)現(xiàn)角度來分析 inject。

inject 原理分析

inject 包中只有 2 個(gè)文件,一個(gè)是 inject.go 文件和一個(gè) inject_test.go 文件,這里我們只需要關(guān)注 inject.go 文件即可。

inject.go 短小精悍,包括注釋和空行在內(nèi)才 157 行代碼,代碼中定義了 4 個(gè)接口,包括一個(gè)父接口和三個(gè)子接口,如下所示:

type Injector interface {
    Applicator
    Invoker
    TypeMapper
    SetParent(Injector)
}

type Applicator interface {
    Apply(interface{}) error
}

type Invoker interface {
    Invoke(interface{}) ([]reflect.Value, error)
}

type TypeMapper interface {
    Map(interface{}) TypeMapper
    MapTo(interface{}, interface{}) TypeMapper
    Get(reflect.Type) reflect.Value
}

Injector 接口是 Applicator、Invoker、TypeMapper 接口的父接口,所以實(shí)現(xiàn)了 Injector 接口的類型,也必然實(shí)現(xiàn)了 Applicator、Invoker 和 TypeMapper 接口:

  • Applicator 接口只規(guī)定了 Apply 成員,它用于注入 struct。
  • Invoker 接口只規(guī)定了 Invoke 成員,它用于執(zhí)行被調(diào)用者。
  • TypeMapper 接口規(guī)定了三個(gè)成員,Map 和 MapTo 都用于注入?yún)?shù),但它們有不同的用法,Get 用于調(diào)用時(shí)獲取被注入的參數(shù)。

另外 Injector 還規(guī)定了 SetParent 行為,它用于設(shè)置父 Injector,其實(shí)它相當(dāng)于查找繼承。也即通過 Get 方法在獲取被注入?yún)?shù)時(shí)會(huì)一直追溯到 parent,這是個(gè)遞歸過程,直到查找到參數(shù)或?yàn)?nil 終止。

type injector struct {
    values map[reflect.Type]reflect.Value
    parent Injector
}

func InterfaceOf(value interface{}) reflect.Type {
    t := reflect.TypeOf(value)

    for t.Kind() == reflect.Ptr {
        t = t.Elem()
    }

    if t.Kind() != reflect.Interface {
        panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
    }

    return t
}

func New() Injector {
    return &injector{
        values: make(map[reflect.Type]reflect.Value),
    }
}

injector 是 inject 包中唯一定義的 struct,所有的操作都是基于 injector struct 來進(jìn)行的,它有兩個(gè)成員 values 和 parent。values 用于保存注入的參數(shù),是一個(gè)用 reflect.Type 當(dāng)鍵、reflect.Value 為值的 map,理解這點(diǎn)將有助于理解 Map 和 MapTo。

New 方法用于初始化 injector struct,并返回一個(gè)指向 injector struct 的指針,但是這個(gè)返回值被 Injector 接口包裝了。

InterfaceOf 方法雖然只有幾句實(shí)現(xiàn)代碼,但它是 Injector 的核心。InterfaceOf 方法的參數(shù)必須是一個(gè)接口類型的指針,如果不是則引發(fā) panic。InterfaceOf 方法的返回類型是 reflect.Type,大家應(yīng)該還記得 injector 的成員 values 就是一個(gè) reflect.Type 類型當(dāng)鍵的 map。這個(gè)方法的作用其實(shí)只是獲取參數(shù)的類型,而不關(guān)心它的值。

示例代碼如下所示:

package main

import (
    "fmt"
    "github.com/codegangsta/inject"
)

type SpecialString interface{}

func main() {
    fmt.Println(inject.InterfaceOf((*interface{})(nil)))
    fmt.Println(inject.InterfaceOf((*SpecialString)(nil)))
}

運(yùn)行結(jié)果如下:

interface {}
main.SpecialString

InterfaceOf 方法就是用來得到參數(shù)類型,而不關(guān)心它具體存儲(chǔ)的是什么值。

func (i *injector) Map(val interface{}) TypeMapper {
    i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
    return i
}

func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
    i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
    return i
}

func (i *injector) Get(t reflect.Type) reflect.Value {
    val := i.values[t]
    if !val.IsValid() && i.parent != nil {
        val = i.parent.Get(t)
    }
    return val
}

func (i *injector) SetParent(parent Injector) {
    i.parent = parent
}

Map 和 MapTo 方法都用于注入?yún)?shù),保存于 injector 的成員 values 中。這兩個(gè)方法的功能完全相同,唯一的區(qū)別就是 Map 方法用參數(shù)值本身的類型當(dāng)鍵,而 MapTo 方法有一個(gè)額外的參數(shù)可以指定特定的類型當(dāng)鍵。但是 MapTo 方法的第二個(gè)參數(shù) ifacePtr 必須是接口指針類型,因?yàn)樽罱K ifacePtr 會(huì)作為 InterfaceOf 方法的參數(shù)。

為什么需要有 MapTo 方法?因?yàn)樽⑷氲膮?shù)是存儲(chǔ)在一個(gè)以類型為鍵的 map 中,可想而知,當(dāng)一個(gè)函數(shù)中有一個(gè)以上的參數(shù)的類型是一樣時(shí),后執(zhí)行 Map 進(jìn)行注入的參數(shù)將會(huì)覆蓋前一個(gè)通過 Map 注入的參數(shù)。

SetParent 方法用于給某個(gè) Injector 指定父 Injector。Get 方法通過 reflect.Type 從 injector 的 values 成員中取出對(duì)應(yīng)的值,它可能會(huì)檢查是否設(shè)置了 parent,直到找到或返回?zé)o效的值,最后 Get 方法的返回值會(huì)經(jīng)過 IsValid 方法的校驗(yàn)。

示例代碼如下所示:

package main

import (
    "fmt"
    "reflect"
    "github.com/codegangsta/inject"
)

type SpecialString interface{}

func main() {
    inj := inject.New()
    inj.Map("C語言中文網(wǎng)")
    inj.MapTo("Golang", (*SpecialString)(nil))
    inj.Map(20)
    fmt.Println("字符串是否有效?", inj.Get(reflect.TypeOf("Go語言入門教程")).IsValid())
    fmt.Println("特殊字符串是否有效?", inj.Get(inject.InterfaceOf((*SpecialString)(nil))).IsValid())
    fmt.Println("int 是否有效?", inj.Get(reflect.TypeOf(18)).IsValid())
    fmt.Println("[]byte 是否有效?", inj.Get(reflect.TypeOf([]byte("Golang"))).IsValid())
    inj2 := inject.New()
    inj2.Map([]byte("test"))
    inj.SetParent(inj2)
    fmt.Println("[]byte 是否有效?", inj.Get(reflect.TypeOf([]byte("Golang"))).IsValid())
}

運(yùn)行結(jié)果如下所示:

字符串是否有效? true
特殊字符串是否有效? true
int 是否有效? true
[]byte 是否有效? false
[]byte 是否有效? true

通過以上例子應(yīng)該知道 SetParent 是什么樣的行為,是不是很像面向?qū)ο笾械牟檎益湥?br />

func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
    t := reflect.TypeOf(f)

    var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func
    for i := 0; i < t.NumIn(); i++ {
        argType := t.In(i)
        val := inj.Get(argType)
        if !val.IsValid() {
            return nil, fmt.Errorf("Value not found for type %v", argType)
        }
        in[i] = val
    }
    return reflect.ValueOf(f).Call(in), nil
}

Invoke 方法用于動(dòng)態(tài)執(zhí)行函數(shù),當(dāng)然執(zhí)行前可以通過 Map 或 MapTo 來注入?yún)?shù),因?yàn)橥ㄟ^ Invoke 執(zhí)行的函數(shù)會(huì)取出已注入的參數(shù),然后通過 reflect 包中的 Call 方法來調(diào)用。Invoke 接收的參數(shù) f 是一個(gè)接口類型,但是 f 的底層類型必須為 func,否則會(huì) panic。

package main

import (
    "fmt"
    "github.com/codegangsta/inject"
)

type SpecialString interface{}

func Say(name string, gender SpecialString, age int) {
    fmt.Printf("My name is %s, gender is %s, age is %d!\n", name, gender, age)
}

func main() {
    inj := inject.New()
    inj.Map("張三")
    inj.MapTo("男", (*SpecialString)(nil))
    inj2 := inject.New()
    inj2.Map(25)
    inj.SetParent(inj2)
    inj.Invoke(Say)
}

運(yùn)行結(jié)果如下:

My name is 張三, gender is 男, age is 25!

上面的例子如果沒有定義 SpecialString 接口作為 gender 參數(shù)的類型,而把 name 和 gender 都定義為 string 類型,那么 gender 會(huì)覆蓋 name 的值。

func (inj *injector) Apply(val interface{}) error {
    v := reflect.ValueOf(val)

    for v.Kind() == reflect.Ptr {
        v = v.Elem()
    }

    if v.Kind() != reflect.Struct {
        return nil
    }

    t := v.Type()

    for i := 0; i < v.NumField(); i++ {
        f := v.Field(i)
        structField := t.Field(i)
        if f.CanSet() && structField.Tag == "inject" {
            ft := f.Type()
            v := inj.Get(ft)
            if !v.IsValid() {
                return fmt.Errorf("Value not found for type %v", ft)
            }
            f.Set(v)
        }
    }
    return nil
}

Apply 方法是用于對(duì) struct 的字段進(jìn)行注入,參數(shù)為指向底層類型為結(jié)構(gòu)體的指針??勺⑷氲那疤崾牵鹤侄伪仨毷菍?dǎo)出的(也即字段名以大寫字母開頭),并且此字段的 tag 設(shè)置為
`inject`

示例代碼如下所示:

package main

import (
    "fmt"
    "github.com/codegangsta/inject"
)

type SpecialString interface{}

type TestStruct struct {
    Name   string `inject`
    Nick   []byte
    Gender SpecialString `inject`
    uid    int           `inject`
    Age    int           `inject`
}

func main() {
    s := TestStruct{}
    inj := inject.New()
    inj.Map("張三")
    inj.MapTo("男", (*SpecialString)(nil))
    inj2 := inject.New()
    inj2.Map(26)
    inj.SetParent(inj2)
    inj.Apply(&s)
    fmt.Println("s.Name =", s.Name)
    fmt.Println("s.Gender =", s.Gender)
    fmt.Println("s.Age =", s.Age)
}

運(yùn)行結(jié)果如下:

s.Name = 張三
s.Gender = 男
s.Age = 26


新聞標(biāo)題:創(chuàng)新互聯(lián)GO教程:Go語言inject庫:依賴注入
文章分享:http://www.5511xx.com/article/ccisiec.html