需求分析:

如在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. Python tarfile模块解压报错 invalid mode ('wb') or filename

    问题原因 在使用tarfile模块解压一份Linux服务器上的打包文件时, 出现了错误提示: IOError: [Errno 22] invalid mode ('wb') or filename. ...

  2. 基于jquery的可查询多级select控件(可记录历史选择)

    一.功能和使用 公司有功能需求,还要一条代码引入的控件,网上找完全符合的控件比较难,寻找所花的时间还不如自己写一个,所以找个空闲时间自己写了一个   控件功能:1.可手动输入查询,也可点击下拉框查询, ...

  3. wordpress域名解析到了网站,但是点击其他页面会出现ip而不是域名

         1.前提域名可以访问你的网站证明解析没问题 2.那就是wp后台的设置问题,将url和站点url改为你的域名http://www.eovision.cc清理缓存即可 亲测可用,如果改了出现页面 ...

  4. js继承方式及其优缺点?

    原型链继承的缺点一是字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数.借用构造函数(类式继承)借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起.所以我们需要 ...

  5. Unit01: Servlet基础 、 HTTP协议

    Unit01: Servlet基础 . HTTP协议 在页面上输出当前时间 package web; import java.io.IOException; import java.io.PrintW ...

  6. javascript基础-js继承

    1.prototype方式 示例:没有使用prototype(下列这些代码只能获取array1数组的总和,而无法对array2数据进行求和) var array1 = new Array(1,4,9, ...

  7. “FPGA+云"助力高性能计算

    用AI防鲨鱼.用AI学写中国书法.用AI预测人类死亡时间.用AI审判罪犯……在人工智能方兴未艾的今天,越来越廉价和普及的AI领域真的是什么都不值钱,除了想象力.那在这无所不能的AI盛世,一定没道理让算 ...

  8. 20165226 实验三 敏捷开发与XP实践

    实验三 敏捷开发与XP实践 实验目的 一.安装alibaba 插件,解决代码中的规范问题.在IDEA中使用工具(Code->Reformate Code)格式化代码,研究一下Code菜单,比较功 ...

  9. POJ 2991 Crane(线段树)

    Crane Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7687   Accepted: 2075   Special J ...

  10. Zabbix 添加 WEB 监控

    添加 WEB Monitorings Web Monitoring是用来监控web程序的,可以监控到web程序的下载速度.返回码及响应时间,还支持把一组连续的web动作作为一个整体来监控. 下面我们以 ...