从java转到go已有3年之余,但是仍旧十分怀念java中的自动注入,于是趁闲暇时间开发了这款golang自动注入工具。
网上搜索了许多自动注入工具,但发现使用起来都有比较繁琐,无法达到java中的那种顺畅,于是根据自身的业务需求开发了一版适用的注入工具,贴合自身代码风格与业务,也期望能满足广泛的使用场景。
本工具主要针对自定义结构体(能够以new()方式创建),以单例方式进行注入,支持循环引用,支持接口类型注入,有需求的小伙伴可以在这基础上继续造轮子,适配自身使用场景
以下是对自动注入工具的简单使用
//声明A,并自动注入结构体B,obj及接口对象inter
type A struct {
a int
b *B `inject:""`
obj *MyStruct `inject:""`
inter MyInterface `inject:""`
}
//声明B,并自动注入A
type B struct {
a *A `inject:""`
b int
}
type MyInterface interface {
Get() string
}
//结构体MyStruct自动注入B
type MyStruct struct {
Value *B `inject:""`
}
func (m *MyStruct) Get() string {
return "test"
}
//绑定接口与实现
func init() {
InterfaceBind[MyInterface](MyInterface(nil), (*MyStruct)(nil))
}
//以上对象声明包含了循环引用,接口注入,是目前业务中service层经常遇到的场景
//简单测试下自动注入情况
func TestCyclicDependence(t *testing.T) {
b := GetBean[*B]()
a := GetBean[*A]()
a.a = 2
b.b = 1
fmt.Println(a.a == b.a.a)
fmt.Println(a.b.b == b.b)
}
//获取到A,B实例,并改变成员属性,可见a,b两个变量中的对象属性相应变更,证实这是一个单例注入,并兼容循环引用
//下面是测试接口注入的情况
func TestInjectInterface(t *testing.T) {
a := GetBean[*A]()
fmt.Println(a.obj.Get() == a.inter.Get())
a.b.b = 2
fmt.Println(a.obj.Value.b == a.b.b)
inter := GetBean[MyInterface]()
fmt.Println(inter.Get() == a.obj.Get())
obj := GetBean[*MyStruct]()
fmt.Println(obj.Get() == inter.Get())
}
整体代码使用反射对注入对象赋值,下面是核心代码
func (b *BeanManger) getBeanAndInit(objVal reflect.Value, objType reflect.Type) {
oldVal := objVal
//初始对象先放入早期缓存中
b.addBean(objVal, objType)
if objVal.Kind() == reflect.Ptr {
objVal = objVal.Elem()
objType = objType.Elem()
}
//对成员变量进行赋值
for i := 0; i < objType.NumField(); i++ {
_, ok := objType.Field(i).Tag.Lookup(injectTag)
if !ok {
continue
}
field := objVal.Field(i)
if field.Type().Kind() == reflect.Interface {
fieldVal := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem()
target, ok := interfaceMap(field.Type())
if !ok {
panic("interface not register")
}
interfaceVal := reflect.New(target)
fieldVal.Set(interfaceVal)
//若早期缓存已存在,直接使用缓存值
bean := b.getEarlyBeanByType(field.Type())
if bean != nil {
val := reflect.ValueOf(bean)
fieldVal.Set(val)
continue
}
b.getBeanAndInit(interfaceVal, interfaceVal.Type())
} else if field.Type().Kind() == reflect.Ptr {
fieldVal := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem()
//拿到nil型指针变量
col := reflect.New(field.Type()).Elem().Interface()
//拿到类型初始值
v := reflect.New(reflect.TypeOf(col).Elem())
fieldVal.Set(v)
bean := b.getEarlyBeanByType(field.Type())
if bean != nil {
val := reflect.ValueOf(bean)
fieldVal.Set(val)
continue
}
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
panic("auto inject is allowed only on structs")
}
b.getBeanAndInit(v, v.Type())
} else {
panic("not support type of inject")
}
}
//对象初始化完毕后加入到缓存中
b.AddBean(b.GetKey(oldVal.Type()), oldVal.Interface())
return
}