mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
fix: 机器文件内容写入导致内容清空、feat: ioc支持根据类型注入
This commit is contained in:
@@ -72,7 +72,7 @@ type App[T model.ModelI] interface {
|
||||
|
||||
// 基础application接口实现
|
||||
type AppImpl[T model.ModelI, R Repo[T]] struct {
|
||||
Repo R // repo接口
|
||||
Repo R `inject:"T"` // repo接口, 根据类型进行注入
|
||||
}
|
||||
|
||||
// 获取repo
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ioc
|
||||
|
||||
import "reflect"
|
||||
|
||||
type ComponentOption func(component *Component)
|
||||
|
||||
// 组件名
|
||||
@@ -13,6 +15,8 @@ func WithComponentName(name string) ComponentOption {
|
||||
type Component struct {
|
||||
Name string // 组件名
|
||||
|
||||
Type reflect.Type // 组件类型
|
||||
|
||||
Value any // 组件实例
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,11 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
InjectTag = "inject"
|
||||
ByTypeComponentName = "T" // 根据类型注入的组件名
|
||||
)
|
||||
|
||||
// 容器
|
||||
type Container struct {
|
||||
mu sync.RWMutex
|
||||
@@ -34,22 +39,25 @@ func (c *Container) Register(bean any, opts ...ComponentOption) {
|
||||
component := NewComponent(bean, opts...)
|
||||
|
||||
componentName := component.Name
|
||||
cType := structx.IndirectType(reflect.TypeOf(component.Value))
|
||||
cType := reflect.TypeOf(component.Value)
|
||||
indirectCType := structx.IndirectType(cType)
|
||||
// 组件名为空,则取组件类型名称作为组件名
|
||||
if componentName == "" {
|
||||
componentName = cType.Name()
|
||||
componentName = indirectCType.Name()
|
||||
component.Name = componentName
|
||||
}
|
||||
|
||||
component.Type = cType
|
||||
|
||||
if _, ok := c.components[componentName]; ok {
|
||||
logx.Warnf("组件名[%s]已经注册至容器, 重复注册...", componentName)
|
||||
logx.Warnf("the component name [%s] has been registered to the container. Repeat the registration...", componentName)
|
||||
}
|
||||
|
||||
logx.Debugf("ioc register : %s = %s.%s", componentName, cType.PkgPath(), cType.Name())
|
||||
logx.Debugf("ioc register : %s = %s.%s", componentName, indirectCType.PkgPath(), indirectCType.Name())
|
||||
c.components[componentName] = component
|
||||
}
|
||||
|
||||
// 注册对象实例的字段含有inject:"xxx"标签或者Setter方法,则注入对应组件实例
|
||||
// Inject 注册对象实例的字段含有注入标签或者Setter方法,则注入对应组件实例
|
||||
func (c *Container) Inject(obj any) error {
|
||||
objValue := reflect.ValueOf(obj)
|
||||
if structx.Indirect(objValue).Kind() != reflect.Struct {
|
||||
@@ -106,49 +114,101 @@ func (c *Container) Get(name string) (any, error) {
|
||||
return component.Value, nil
|
||||
}
|
||||
|
||||
// 根据实例字段的inject:"xxx"标签进行依赖注入
|
||||
// GetByType 根据组件实例类型获取组件实例
|
||||
func (c *Container) GetByType(fieldType reflect.Type) (any, error) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
for _, component := range c.components {
|
||||
if component.Type.AssignableTo(fieldType) {
|
||||
return component.Value, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("component type not found: " + fmt.Sprintf("%s.%s", fieldType.PkgPath(), fieldType.Name()))
|
||||
}
|
||||
|
||||
// injectWithField 根据实例字段的inject:"xxx"或injectByType:""标签进行依赖注入
|
||||
func (c *Container) injectWithField(objValue reflect.Value) error {
|
||||
objValue = structx.Indirect(objValue)
|
||||
objType := objValue.Type()
|
||||
|
||||
logx.Debugf("start ioc inject with field: %s.%s", objType.PkgPath(), objType.Name())
|
||||
|
||||
for i := 0; i < objType.NumField(); i++ {
|
||||
field := objType.Field(i)
|
||||
fieldValue := objValue.Field(i)
|
||||
|
||||
componentName, ok := field.Tag.Lookup("inject")
|
||||
// 检查字段是否是通过组合包含在当前结构体中的,即嵌套结构体
|
||||
if field.Anonymous && structx.IndirectType(field.Type).Kind() == reflect.Struct {
|
||||
c.injectWithField(fieldValue)
|
||||
continue
|
||||
}
|
||||
|
||||
componentName, ok := field.Tag.Lookup(InjectTag)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// inject tag字段名为空则默认为字段名
|
||||
if componentName == "" {
|
||||
componentName = field.Name
|
||||
}
|
||||
|
||||
injectInfo := fmt.Sprintf("ioc field inject [%s -> %s.%s#%s]", componentName, objType.PkgPath(), objType.Name(), field.Name)
|
||||
logx.Debugf(injectInfo)
|
||||
|
||||
component, err := c.Get(componentName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s error: %s", injectInfo, err.Error())
|
||||
}
|
||||
|
||||
// 判断字段类型与需要注入的组件类型是否为可赋值关系
|
||||
componentType := reflect.TypeOf(component)
|
||||
if !componentType.AssignableTo(field.Type) {
|
||||
componentType = structx.IndirectType(componentType)
|
||||
return fmt.Errorf("%s error: 注入类型不一致(期望类型->%s.%s, 组件类型->%s.%s)", injectInfo, field.Type.PkgPath(), field.Type.Name(), componentType.PkgPath(), componentType.Name())
|
||||
}
|
||||
|
||||
fieldValue := objValue.Field(i)
|
||||
if !fieldValue.IsValid() || !fieldValue.CanSet() {
|
||||
// 不可导出变量处理
|
||||
fieldPtrValue := reflect.NewAt(fieldValue.Type(), fieldValue.Addr().UnsafePointer())
|
||||
fieldValue = fieldPtrValue.Elem()
|
||||
if !fieldValue.IsValid() || !fieldValue.CanSet() {
|
||||
return fmt.Errorf("%s error: 字段无效或为不可导出类型", injectInfo)
|
||||
// 如果组件名为指定的根据类型注入值,则根据类型注入
|
||||
if componentName == ByTypeComponentName {
|
||||
if err := c.injectByType(objType, field, fieldValue); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
fieldValue.Set(reflect.ValueOf(component))
|
||||
if err := c.injectByName(objType, field, fieldValue, componentName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// injectByName 根据实例组件名进行依赖注入
|
||||
func (c *Container) injectByName(structType reflect.Type, field reflect.StructField, fieldValue reflect.Value, componentName string) error {
|
||||
// inject tag字段名为空则默认为字段名
|
||||
if componentName == "" {
|
||||
componentName = field.Name
|
||||
}
|
||||
|
||||
injectInfo := fmt.Sprintf("ioc field inject [%s -> %s.%s#%s]", componentName, structType.PkgPath(), structType.Name(), field.Name)
|
||||
logx.Debugf(injectInfo)
|
||||
|
||||
component, err := c.Get(componentName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s error: %s", injectInfo, err.Error())
|
||||
}
|
||||
|
||||
// 判断字段类型与需要注入的组件类型是否为可赋值关系
|
||||
componentType := reflect.TypeOf(component)
|
||||
if !componentType.AssignableTo(field.Type) {
|
||||
indirectComponentType := structx.IndirectType(componentType)
|
||||
return fmt.Errorf("%s error: injection types are inconsistent(Expected type -> %s.%s, Component type -> %s.%s)", injectInfo, field.Type.PkgPath(), field.Type.Name(), indirectComponentType.PkgPath(), indirectComponentType.Name())
|
||||
}
|
||||
|
||||
if err := setFieldValue(fieldValue, component); err != nil {
|
||||
return fmt.Errorf("%s error: %s", injectInfo, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// injectByType 根据实例类型进行依赖注入
|
||||
func (c *Container) injectByType(structType reflect.Type, field reflect.StructField, fieldValue reflect.Value) error {
|
||||
fieldType := field.Type
|
||||
|
||||
injectInfo := fmt.Sprintf("ioc field inject by type [%s.%s -> %s.%s#%s]", fieldType.PkgPath(), fieldType.Name(), structType.PkgPath(), structType.Name(), field.Name)
|
||||
logx.Debugf(injectInfo)
|
||||
|
||||
component, err := c.GetByType(fieldType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s error: %s", injectInfo, err.Error())
|
||||
}
|
||||
|
||||
if err := setFieldValue(fieldValue, component); err != nil {
|
||||
return fmt.Errorf("%s error: %s", injectInfo, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -174,7 +234,7 @@ func (c *Container) injectWithMethod(objValue reflect.Value) error {
|
||||
logx.Debugf(injectInfo)
|
||||
|
||||
if method.Type.NumIn() != 2 {
|
||||
logx.Warnf("%s error: 方法入参不为1个, 无法进行注入", injectInfo)
|
||||
logx.Warnf("%s error: the method cannot be injected if it does not have one parameter", injectInfo)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -188,7 +248,7 @@ func (c *Container) injectWithMethod(objValue reflect.Value) error {
|
||||
expectedComponentType := method.Type.In(1)
|
||||
if !componentType.AssignableTo(expectedComponentType) {
|
||||
componentType = structx.IndirectType(componentType)
|
||||
return fmt.Errorf("%s error: 注入类型不一致(期望类型->%s.%s, 组件类型->%s.%s)", injectInfo, expectedComponentType.PkgPath(), expectedComponentType.Name(), componentType.PkgPath(), componentType.Name())
|
||||
return fmt.Errorf("%s error: injection types are inconsistent(Expected type -> %s.%s, Component type -> %s.%s)", injectInfo, expectedComponentType.PkgPath(), expectedComponentType.Name(), componentType.PkgPath(), componentType.Name())
|
||||
}
|
||||
|
||||
method.Func.Call([]reflect.Value{objValue, reflect.ValueOf(component)})
|
||||
@@ -196,3 +256,17 @@ func (c *Container) injectWithMethod(objValue reflect.Value) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFieldValue(fieldValue reflect.Value, component any) error {
|
||||
if !fieldValue.IsValid() || !fieldValue.CanSet() {
|
||||
// 不可导出变量处理
|
||||
fieldPtrValue := reflect.NewAt(fieldValue.Type(), fieldValue.Addr().UnsafePointer())
|
||||
fieldValue = fieldPtrValue.Elem()
|
||||
if !fieldValue.IsValid() || !fieldValue.CanSet() {
|
||||
return errors.New("the field is invalid or a non-exportable type")
|
||||
}
|
||||
}
|
||||
|
||||
fieldValue.Set(reflect.ValueOf(component))
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user