在golang中如何通过配置文件中的名称获取接口的实例

I want to know how can I inject dependency flexibly in golang just like java spring, Which is if I want to change a instance of an interface all I have to do is just to change some config file.

First I want to find some function like getTypeByName(), so I can just give a struct name like "mypkg.structName" in config file and load that struct by the function, but it seems that there is no such function in golang.

And now the most doable way i can think is to create a loader model to generate a struct register file to load all custom struct.

But I want to know is there have a easier way to do that, all is there some golang-style way to implement this flexibility?

I think your approach is correct, since you can not instantiate a type if it's not defined during compilation. Otherwise, you need to use plugin.

I'm not sure whether it's easier or not, but if you want the custom structs being organized in different packages, you can use similar approach as database/sql.

  1. Define a factory package which consists of Factory interface and manage known factories. This package should export Register function for registering custom factory, and New function for creating instance of a given type. For example:

    package factory
    
    import (
        "strings"
        "sync"
    )
    
    type Factory interface {
        New(name string) (interface{}, bool)
    }
    
    var (
        mu        sync.RWMutex
        factories = make(map[string]Factory)
    )
    
    func Register(pkgName string, f Factory) {
        mu.Lock()
        defer mu.Unlock()
    
        if f == nil {
            panic("Factory is nil")
        }
        if _, exist := factories[pkgName]; exist {
            panic("Factory already registered")
        }
    
        factories[pkgName] = f
    }
    
    func New(typeName string) (interface{}, bool) {
        items := strings.Split(typeName, ".")
        if len(items) >= 2 {
            mu.RLock()
            defer mu.RUnlock()
            if f, exist := factories[items[0]]; exist {
                return f.New(items[1])
            }
        }
        return nil, false
    }
    
  2. Create package which consists of your custom struct and implementing the Factory interface. Register the factory during initalization i.e. in init() function, e.g

    package pkga
    
    import "path/to/your/factory"
    
    type thisFactory struct {
    }
    
    type Alpha struct{}
    type Beta struct{}
    type Gamma struct{}
    
    func (f *thisFactory) New(name string) (interface{}, bool) {
        switch name {
        case "Alpha":
            return &Alpha{}, true
        case "Beta":
            return &Beta{}, true
        case "Gamma":
            return &Gamma{}, true
        }
        return nil, false
    }
    
    func init() {
        factory.Register("pkga", &thisFactory{})
    }
    
  3. Repeat step (2) as you need.

  4. Finally, in your main package, you can create an instance as follows:

    package main
    
    import (
        "fmt"
    
        "path/to/your/factory"
        _ "path/to/custom/pkga"
        _ "path/to/custom/pkgb"
        //add more packages ...
    )
    
    func main() {
        a, _ := factory.New("pkga.Alpha")
        b, _ := factory.New("pkgb.Beta")
        c, _ := factory.New("pkga.Gamma")
    
        fmt.Printf("%T %T %T
    ", a, b, c)
    }
    

Update: a working example is available here.