写点什么

go 实现类似 spring BeanUtil 工具

作者:Z.K
  • 2022-12-06
    广东
  • 本文字数:1516 字

    阅读完需:约 5 分钟

纯递归实现深拷贝,src 和 dst 必须是结构体指针。


func CopyTo(src any, dst any) error {	srcPtrTyp := reflect.TypeOf(src)	if srcPtrTyp.Kind() != reflect.Pointer {		return newErrTypeError(srcPtrTyp)	}	srcTyp := srcPtrTyp.Elem()	if srcTyp.Kind() != reflect.Struct {		return newErrTypeError(srcTyp)	}	dstPtrTyp := reflect.TypeOf(dst)	if dstPtrTyp.Kind() != reflect.Pointer {		return newErrTypeError(dstPtrTyp)	}	dstTyp := dstPtrTyp.Elem()	if dstTyp.Kind() != reflect.Struct {		return newErrTypeError(dstTyp)	}
srcValue := reflect.ValueOf(src).Elem() dstValue := reflect.ValueOf(dst).Elem()
return copyStruct(srcTyp, srcValue, dstTyp, dstValue)}
func copyStruct(srcTyp reflect.Type, srcValue reflect.Value, dstTyp reflect.Type, dstValue reflect.Value) error { srcFieldNameIndex := make(map[string]int, 0) for i := 0; i < srcTyp.NumField(); i++ { fTyp := srcTyp.Field(i) if !fTyp.IsExported() { continue } srcFieldNameIndex[fTyp.Name] = i }
for i := 0; i < dstTyp.NumField(); i++ { fTyp := dstTyp.Field(i) if !fTyp.IsExported() { continue } if idx, ok := srcFieldNameIndex[fTyp.Name]; ok { if err := copyStructField(srcTyp, srcValue, dstTyp, dstValue, idx, i); err != nil { return err } } } return nil}
func copyStructField( srcTyp reflect.Type, srcValue reflect.Value, dstTyp reflect.Type, dstValue reflect.Value, srcFieldIndex int, dstFieldIndex int) error {
srcFieldType := srcTyp.Field(srcFieldIndex) dstFieldType := dstTyp.Field(dstFieldIndex) if srcFieldType.Type.Kind() != dstFieldType.Type.Kind() { return newErrKindNotMatchError(srcFieldType.Type.Kind(), dstFieldType.Type.Kind(), srcFieldType.Name) } srcFieldValue := srcValue.Field(srcFieldIndex) dstFieldValue := dstValue.Field(dstFieldIndex)
if srcFieldType.Type.Kind() == reflect.Pointer { if srcFieldValue.IsNil() { return nil } if dstFieldValue.IsNil() { //设置dstFieldType指向类型的零值 dstFieldValue.Set(reflect.New(dstFieldType.Type.Elem())) } return copyData(srcFieldType.Type.Elem(), srcFieldValue.Elem(), dstFieldType.Type.Elem(), dstFieldValue.Elem(), srcFieldType.Name) }
return copyData(srcFieldType.Type, srcFieldValue, dstFieldType.Type, dstFieldValue, srcFieldType.Name)}
func copyData( srcTyp reflect.Type, srcValue reflect.Value, dstTyp reflect.Type, dstValue reflect.Value, fieldName string,) error { if srcTyp.Kind() == reflect.Pointer { return newErrMultiPointer(fieldName) } if srcTyp.Kind() != dstTyp.Kind() { return newErrKindNotMatchError(srcTyp.Kind(), dstTyp.Kind(), fieldName) }
if isShadowCopyType(srcTyp.Kind()) { // 内置类型,但不匹配,如别名、map和slice if srcTyp != dstTyp { return newErrTypeNotMatchError(srcTyp, dstTyp, fieldName) } if dstValue.CanSet() { //完成内置类型字段拷贝 dstValue.Set(srcValue) } } else if srcTyp.Kind() == reflect.Struct { return copyStruct(srcTyp, srcValue, dstTyp, dstValue) } return nil}
复制代码


实现这样一个工具,需要注意的点:

  1. 纯反射实现,可能存在性能问题

  2. 只能复制公共字段

用户头像

Z.K

关注

还未添加个人签名 2020-07-09 加入

还未添加个人简介

评论

发布
暂无评论
go实现类似spring BeanUtil工具_Z.K_InfoQ写作社区