需求分析:

如在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. MySQL-Proxy 读写分离、同步延时问题解决方案

    MySQL-Proxy 读写分离.同步延时问题解决方案 使用MySQL将读写请求转接到主从Server. 一 安装MySQL Proxy MySQL Proxy的二进制版非常方便,下载解压缩后即用. ...

  2. as3 阻止后续侦听器

    public class Test1 extends Sprite { private var spr:Sprite; private var spr2:Sprite; public function ...

  3. Linux环境下搭建Git仓库

    1.安装Git $ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-devel $ yum ...

  4. 0基础自学php教程

    轻松搞定网页设计之html 一.HTML介绍 1. HTML概念 HTML(Hyper Text Markup Language),即超文本标记语言.是目前网络上应用最为广泛的语言,是构成网页文档的主 ...

  5. 如何配置Python环境

    (1) 下载:请在Python官网下载页面(https://www.python.org/downloads/)选择合适的版本(建议选择3.5.2版)的链接,在该版本的下载页面选择合适的安装文件:64 ...

  6. java bean 合并

    package com.hainabo.mgcmall.util; import java.beans.BeanInfo;import java.beans.Introspector;import j ...

  7. 018:InnoDB 存储引擎、表空间

    目录 一.InnoDB 存储引擎 1. InnoDB的历史 2. InnoDB的特点 3. InnoDB存储引擎的文件 3.1 概述 3.2 InnoDB - 表空间 3.3 General表空间 3 ...

  8. python学习(二十) Python 中的比较:is 与 ==

    Python 中的比较:is 与 == 在 Python 中会用到对象之间比较,可以用 ==,也可以用 is .但是它们的区别是什么呢? is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象 ...

  9. node的close

    在http.ServerResponse对象的end方法被调用之前,如果连接被中断,将触发http.ServerResponse对象的close事件. var http=require("h ...

  10. sql,groupby以后取每组前三行

    --> 生成测试数据: #TIF OBJECT_ID('tempdb.dbo.#T') IS NOT NULL DROP TABLE #T CREATE TABLE #T (ID VARCHAR ...