需求分析:

如在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模块--config

    一.创建文件 ##-----------------创建数据表-------------------------- import configparser config = configparser. ...

  2. 几种分布式调用技术的比较 -- RPC VS REST

    我之前在传统IT公司干活,后来来了互联网,感受到了很多不同,其中有一点就是两者使用到的技术有一些差别.比如说分布式调用技术. 我在的这家公司内部的服务架构是基于Thrift的,服务基于Thrift进行 ...

  3. Java多线程编程核心技术,第一章

    1,Java并发--详解this与Thread.currentThread()的区别:https://blog.csdn.net/championhengyi/article/details/7666 ...

  4. Tomcat(64位)免安装版的环境安装与配置

    本篇博客主要介绍Tomcat(64位)免安装版的环境安装与配置,该篇文章同样适合于32位Tomcat免安装版的环境安装与配置. 该篇博客中的大部分内容同百度经验中的<出现unable to op ...

  5. python中高阶函数学习笔记

    什么是高阶函数 变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数 def fun(x, y, f): print f(x), f(y) fun ...

  6. ASP.NET-自定义HttpModule与HttpHandler介绍

    ASP.NET对请求处理的过程:当请求一个*.aspx文件的时候,这个请求会被inetinfo.exe进程截获,它判断文件的后缀(aspx)之后,将这个请求转交给 ASPNET_ISAPI.dll,A ...

  7. Netty--JDK序列化编解码传输对象

    使用JDK序列化不需要额外的类库,只需要实现Serializable即可,但是序列化之后的码流只有Java才能反序列化,所以它不是跨语言的,另外由于Java序列化后码流比较大,效率也不高,所以在RPC ...

  8. Eclipse中创建新的Spring Boot项目

    本文转载自:http://blog.csdn.net/clementad/article/details/51334064 简单几步,在Eclipse中创建一个新的spring Boot项目: 1.E ...

  9. 杂项:Vue.js

    ylbtech-杂项:Vue.js Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的渐进式框架.Vue.js 的目标是通过尽可能简单的 API 实现响应的数据 ...

  10. 初学者手册-Sublime Text常用快捷键

    Alt + F3 :找出当前文档中所有被划选的词语,若文档很大的话,可能会导致Sublime Text崩溃. Ctrl + kkk :删除当前行光标至行尾的所有内容. End: 光标跳至行尾. Hom ...