Golang:使用reflect实现Spring的自动装配

先上最终效果,一段测试代码:

package wind

import (
	"testing"
	"reflect"
	"fmt"
)

type Person struct {
	Name string `@:"Autowired"`
}

func (p *Person) hello() {
	fmt.Println("Hello,", p.Name)
}

func TestWind(t *testing.T) {
	_type := reflect.TypeOf(Person{})
	bf := CreateBeanFactory()
	err := bf.RegisterBean(_type)
	if err != nil {
		fmt.Println(err)
	} else if bean, ok := bf.GetBean("Person"); ok {
		if p, ok := bean.(Person); ok {
			p.hello()
		}
	}
}

根据上面的代码,可以知道我们自动装配的流程如下:

  • 创建BeanFactory
  • 获取Person的反射类型,并将其注册到BeanFactory中
  • 根据名字获取Bean实例

首先定义BeanFactory和Bean

type Bean struct {
	Name string
	beanType reflect.Type
}

type BeanFactory struct {
	beans map[string]Bean
}

这两个定义很简单,BeanFactory使用一个map存储所以注册的Bean
顺带实现CreateBeanFactory

func CreateBeanFactory() *BeanFactory {
	return &BeanFactory{beans: make(map[string]Bean)}
}

实现RegisterBean

func (bf *BeanFactory) RegisterBean(t reflect.Type) (err error) {
	if t.Kind() != reflect.Struct {
		err = errors.New("argument must be a Struct")
	} else {
		beanName := t.Name()
		bf.beans[beanName] = Bean{Name: beanName, beanType: t}
	}
	return
}

这里简单解释一下,首先判断传入的反射类型是不是reflect.Struct,如果不是的话直接报错。是的话则创建一个Bean,把它存到map里

实现GetBean

关键来了,Golang不支持注解,我的思路是用Struct Tag存储@Autowired注解,所以我们要扫描注册的BeanType的所有字段的Tag:

func (bf *BeanFactory) GetBean(name string) (ins interface{}, ok bool) {
	bean, ok := bf.beans[name]
	if ok {
		// reflect.New返回的是一个ptr类型的Value,可以用reflect.TypeOf去验证
		// 这里调用Elem()可以理解为对一个指针的取值操作
		e := reflect.New(bean.beanType).Elem()
		// 根据BeanType去扫描Bean的所有字段的Tag
		_type := bean.beanType
		if numField := _type.NumField(); numField > 0 {
			for i := 0; i < numField; i++ {
				f := _type.Field(i)
				// findAllAnnotations会从Tag中解析出所有名字为"@"的pair
				// 比如`@:"Autowired" @:"GetMapping('/get')"`会解析成["Autowired", "GetMapping('/get')"],一个字符串数组
				if annos, ok := bf.findAllAnnotations(f.Tag); ok {
					for _, anno := range annos {
						// 这里就简单的支持一下Autowired注解
						if anno == "Autowired" {
							switch f.Type.Kind() {
							case reflect.String:
								// 直接注入固定值
								e.Field(i).SetString("Luncert")
							default:
								fmt.Println("Unknown type")
							}
						}
					} 
				}
			}
		}
		ins = e.Interface()
	}
	return
}

到此核心代码就都完成了。
详细代码见Github
更多关于reflect的知识参考:
Golang Reflect反射
Golang学习笔记(11)——反射

猜你喜欢

转载自blog.csdn.net/qq_37242224/article/details/88728365
今日推荐