Golang反射による依存注入

3278 ワード

Golang反射による依存注入
Coding/Golang #Golang #Golang/reflect
依存注入
本人はJavaGolangに転入するとJavaの思考を持ち込むので、注入に依存するのは良いもので、概念はあまり述べません.
構想
  • 注入対象オブジェクトを含むインスタンスを作成し、Map
  • を格納する.
  • は、他のオブジェクトに注入する必要があるオブジェクトインスタンスを作成し、このMap
  • にも格納する.
  • オブジェクト登録が完了すると、Injectメソッドが呼び出され、InjectはMap全体を遍歴し、オブジェクト内のすべての注入する属性を探し出し、
  • に注入する.
    構造体改造前のコード
    func main(){
        runner := &Runner{}
        eater := &Eater{}
        people := NewPeople(runner, eater)
        people.Run.Run()
    }
    func NewPeople(run IRun, eat IEat) IPeople {
        return & People{
            Run: run,
            Eat: eat,
        }
    }
    type People struct {
        Run IRun
        Eat IEat
    }
    

    構造体改造後のコード
    func main(){
        core.register("run", &Runner{}) // run     Runner
        core.register("eat", &Eater{}) // eat     Eater
        people := &People{}
        core.autoRegister(people) // *main.People     People
        core.inject() //    Tag:"auto"     
        people.Run.Run()
    }
    type People struct {
        Run IRun `auto:"run"`
        Eat IEat `auto:"eat"`
    }
    

    インプリメンテーション
    Tag
    Tag autoを使用して、run名で登録されたオブジェクトを取り出して注入するなど、自動注入が必要なオブジェクトをマークします.フィールドを巡回して、すべてのTagがauto:"run"のフィールドを見つけます.
    value := reflect.ValueOf(obj)
    if value.Kind() == reflect.Ptr {
        value = value.Elem()
    }
    for i := 0; i < value.NumField(); i++ {
        name := value.Type().Field(i).Tag.Get("auto")
        //    tag auto   
    }
    

    はんしゃちゅうにゅう
    ここでPublicプロパティの値を変更するのは簡単です.
    field := value.Field(i) //     
    field.Set(value) //    
    

    しかし、値がPrivateである場合、Setを呼び出すとPanicが発生し、メソッドautoを使用して、主な問題がPrivateオブジェクトに値を設定する方法であるかを判断することができます.
    field := value.Field(i)
    field = reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem()
    field.Set(value)
    

    これらはすべて標準ライブラリの中の方法で、派手なものは存在しません.
    ふろく
    import (
        "fmt"
        "reflect"
        "unsafe"
    )
    
    const (
        InjectorTag = "auto"
    )
    
    var objs map[string]reflect.Value
    
    func init() {
        objs = make(map[string]reflect.Value, 10)
    }
    
    // Register     
    func Register(name string, v interface{}) {
        objs[name] = reflect.ValueOf(v)
    }
    
    // AutoRegister     
    func AutoRegister(v interface{}) {
        rv := reflect.ValueOf(v)
        Register(rv.Type().String(), rv)
    }
    
    // Get       
    func Get(key string) interface{} {
        v, ok := objs[key]
        if ok {
            return v.Interface()
        }
        return nil
    }
    
    // Remove       
    func Remove(key string) {
        delete(objs, key)
    }
    
    func Inject() {
        for _, v := range objs {
            value := v
            if value.Kind() == reflect.Ptr {
                value = value.Elem()
            }
            for i := 0; i < value.NumField(); i++ {
                name := value.Type().Field(i).Tag.Get(InjectorTag)
                temp, ok := objs[name]
                if ok {
                    field := value.Field(i)
                    if field.CanSet() {
                        field.Set(temp)
                    } else {
                        field = reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem()
                        field.Set(temp)
                    }
                }
            }
        }
    }