需求分析:

如在rocketmq的网络通信中,所有通信数据包以如下形式传输: (注:rocketmq的java结构体,这里使用了go形式表示)

type RemotingCommand struct {
//header
Code int `json:"code"`
Language string `json:"language"`
Version int `json:"version"`
Opaque int32 `json:"opaque"`
Flag int `json:"flag"`
Remark string `json:"remark,omitempty"`
ExtFields map[string]string `json:"extFields"`
//body
Body []byte `json:"body,omitempty"`
}

其中,ExtFields 表示用户自定义数据包,如:在某次通信中传输的 ExtFields 的内容如下,接收对象为 MyResponseHeader 型。

//ExtFields 数据内容
extFields := make(map[string]string)
extFields ["result"] = "true"
extFields ["answer"] = "1234" //MyResopnseHeader 接口体
type MyResponseHeader struct {
Result bool
Answer int64
}

将 “extFields ” 转化为 MyResponseHeader 型过程中,需要将string型数据分别转换为 bool、int64等类型。

另外,不同的remotingCommand包接收到数据后需要解析成不同的结构体数据, 如何使用一个方式与统一解析数据呢? 解决这个问题需要用到反射。本文根据这个问题,对go中的反射知识进行了简单实践,具体内容如下:

涉及到的反射知识点补充

1.reflect.Value

reflect.ValueOf()的返回值类型为reflect.Value,表示值的真实内容。

var i int = 123
var s = "abc"
fmt.Println(reflect.ValueOf(i)) // 123
fmt.Println(reflect.ValueOf(s)) // abc

2.reflect.Value值的设置

go中不能直接对Value进行赋值操作,如对上述变量 s 进行赋值,首先需要拿到 s 值的指针,然后拿到该指针的reflect.Value,指针的reflect.Value调用Value.Elem()后对对应到 s 值对象,继而可以对 s 进行赋值操作。

value赋值的例子:

func main(){
var i int = 123
fe := reflect.ValueOf(&i).Elem() //必须是指针的Value才能调用Elem
fmt.Println(fe)   // 123
fmt.Println(fe.CanSet()) // true
fe.SetInt(456)
fmt.Println(i) //456
}

3.reflect.Type.Kind 与 reflect.Value.Kind

它返回的是对象的基本类型,例如 Float32、Float64、int32、int64、Slice、Bool、Complex64、Array、chan、Func、Interface、Map 等等。

4.reflect.Type.Filed 与 relfect.Type.Filed

前者放回的是一个StructFiled对象。后者返回的还是一个Value对象

type StructField struct {
Name string // name 常用
PkgPath string Type Type // field type 常用
Tag StructTag // field tag string
Offset uintptr // offset within struct, in bytes
Index []int // index sequence for Type.FieldByIndex
Anonymous bool // is an embedded field
}

5.reflect.Type.FiledByName 与 reflect.Value.FileByName

前者返回一个StructFiled对象,后者返回的还是一个Value对象

具体实现过程

参考rocketmq的思路,先定义一个 CustomHeader接口,自定义包实现该接口,然后定义一个解析包的方法,该方法中包括go反射的运用。

自定义数据包:

type CustomHeader interface {
CheckFields() error
}

结构体实现了 CustomHeader 接口:

type MyResponseHeader struct {
Result bool
Answer int64
} func (t *MyResponseHeader) CheckFields() error {
return nil
}

转化测试:

//ExtFields 数据内容
extFields := make(map[string]string)
extFields ["result"] = "true"
extFields ["answer"] = "1234"
err := DecodeCustomHeader(extFields, myResponseHeader)
if err != nil {
  panic(err.Error())
}
fmt.Printf("myResponseHeader.Result = %v \nmyResponseHeader.Answer = %d\n", myResponseHeader.Result, myResponseHeader.Answer) 

结果:

myResponseHeader.Result = true
myResponseHeader.Answer = 1234

DecodeCustomHeader代码如下:

func DecodeCustomHeader(extFields map[string]string, commandCustomHeader CustomHeader) error {
structValue := reflect.ValueOf(commandCustomHeader).Elem() for k, v := range extFields {
err := reflectSturctSetField(structValue, firstLetterToUpper(k), v)
if err != nil {
return err
}
} return nil
} // 支持string int8 int16 int int32 int64 uint8 uint16 uint32 uint64 bool,非string类型将进行转换
func reflectSturctSetField(structValue reflect.Value, name string, value string) error {
structFieldValue := structValue.FieldByName(name) if !structFieldValue.IsValid() {
return errors.Errorf("No such field: %s in obj", name)
} if !structFieldValue.CanSet() {
return errors.Errorf("Cannot set %s field value", name)
} structFieldType := structFieldValue.Type()
switch structFieldType.Kind() {
case reflect.String:
structFieldValue.SetString(value)
case reflect.Int8:
fallthrough
case reflect.Int16:
fallthrough
case reflect.Int32:
fallthrough
case reflect.Int64:
fallthrough
case reflect.Int:
ival, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return errors.Wrap(err, 0)
}
structFieldValue.SetInt(ival)
case reflect.Uint8:
fallthrough
case reflect.Uint16:
fallthrough
case reflect.Uint32:
fallthrough
case reflect.Uint64:
ival, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return errors.Wrap(err, 0)
}
structFieldValue.SetUint(ival)
case reflect.Bool:
bval, err := strconv.ParseBool(value)
if err != nil {
return errors.Wrap(err, 0)
}
structFieldValue.SetBool(bval)
default:
return errors.Errorf("Provided value type didn't match obj field type")
} return nil
} // 首字母大写
func firstLetterToUpper(s string) string {
if len(s) > 0 {
b := []byte(s)
if b[0] >= 'a' && b[0] <= 'z' {
b[0] = b[0] - byte(32)
s = string(b)
}
} return s
}

只要实现了CustomHeader接口的结构体,调用DecodeCustomHeader方法,就可以获取对应的数据了。另外根据业务需要,可以扩展 reflectSturctSetField 方法。(完)

go反射实例的更多相关文章

  1. ObjectTools反射实例

    ObjectTools反射实例 package com.shitou.deposit.chinapnr.utils; import org.apache.commons.logging.Log; im ...

  2. 类的反射实例(servlet的抽取)

    类的反射实例 具体以后我们写的时候不用写BaseServlet,因为各种框架都已经给我们写好了 所以,user对应的servlet的界面长这样:

  3. C#反射实例应用--------获取程序集信息和通过类名创建类实例

    AppDomain.CurrentDomain.GetAssemblies();获取程序集,但是获取的只是已经加载的dll,引用的获取不到. System.Reflection.Assembly.Ge ...

  4. PHP API反射实例

    *反射是操纵面向对象范型中元模型的API,其功能十分强大,可帮助我们构建复杂,可扩展的应用.其用途如:自动加载插件,自动生成文档,甚至可用来扩充PHP语言.php反射api由若干类组成,可帮助我们用来 ...

  5. java反射 实例

    首先介绍几个概念: 1.Java反射的概念 反射含义:可以获取正在运行的Java对象. 2.Java反射的功能 1)可以判断运行时对象所属的类 2)可以判断运行时对象所具有的成员变量和方法 3)通过反 ...

  6. c# 类的反射实例 (GetType().Invoke().GetMethod().CreateInstance())

    原文:http://www.cnblogs.com/chenwei19/archive/2009/02/04/1384034.html Class1和Form 窗体在同一个命名空间 using Sys ...

  7. C#反射实例(一) 利用反射使用类库

    在网上查找了不少的资料,可以说大同小异,概念性的东西网上一搜一堆,今天把反射的东西整理了一下,供大家使用,我保证我这里是最全面的东西,当然也是基础的东西,在学好了这一切的基础上,大家可以学习反射的具体 ...

  8. Java 反射实例

    实体类:Userpackage com.reflect.model; public class User{ private User(int id, String username, String p ...

  9. C# 反射实例

    1.接口 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ...

随机推荐

  1. .ncx文件剖析

    ncx文件是epub电子书的又一个核心文件,用于制作电子书的目录,其文件的命名通常为toc.ncx. ncx文件是一个XML文件,该标准由DAISY Consortium发布(参见http://www ...

  2. jdk1.8新特性之lambda表达式

    lambda表达式其实就是指一个匿名函数,应用最广泛的就是匿名内部类的简化.在jdk1.8之前,我们定义一个匿名内部类可能需要写一大坨代码,现在有了lambda之后,可以写的很简洁了.但不是说lamb ...

  3. erlang的lists笔记

    一般循环用在遍历列表的时候,erlang有lists模块直接支持遍历,不需要自己写尾递归遍历list lists:foreach 用来遍历列表,不保存结果,最后一次返回ok lists:map 遍历列 ...

  4. springmvc之获取参数

    1.导包,四大核心包,一个切面包(AOP),logging,web,springmvc 2.配置文件,核心代码如下: web.xml <servlet> <servlet-name& ...

  5. redis 命令集

    进入客户端 /usr/local/bin/redis-cli 选择数据库 select index (0-15) 退出 quit

  6. dubbox实现REST服务

    一.dubbox的由来 dubbox是当当网基于dubbo的基础上开发的扩展版,也可以认为是dubbo的升级版,根据当前互联网的应用需求,增加了很多扩展的功能. dubbox并没有发布到maven中央 ...

  7. Java文件的写入

    写文件与读文件类似,可以是以字节为单位写入,可以是以字符为单位写入. 对应读操作FileOutputStream是以字节为单位进行写入的: FileOutputStream fileOutputStr ...

  8. python 线程/进程模块

    线程的基本使用: import threading # ###################### 1.线程的基本使用 def func(arg): print(arg) t = threading ...

  9. Charles修改返回值的方法(构造返回值最大值的情况,比如100,99) (自己没有试过)

    第一步:save respond到电脑 第二步:打开文件,修改相应的参数 第三步:导入修改后的文件 第四步:手机刷新数据,查看结果

  10. Java经典练习题_Day03

    一.选择 B    D(死循环)    E(switch)    BC 二.编程 1.读入一个整数,表示一个人的年龄. 如果小于6岁,则输出“儿童”, 6岁到13岁,输出“少儿”;  14岁到18岁, ...