golang 自动注入工具与反射应用

从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
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值