在我们的业务中,尤其涉及到后台业务,在我们不用考虑性能的情况下,我们写后台框架的时候,可能会遇到这样的一些情况,如何通过某些struct名和方法名传递进来执行不同的逻辑。这个时候我想的是go的反射是最好的实现这种功能,当然在go里面也可以通过定义配置来实现进入动态进入不同的struct名和方法名,或者其他方式(如果你有更好的方式,可以互相交流)。
下面我们来讲一个例子
假如前端传递UserController和GetName,后端通过UserController和GetName调用struct为UserController的GetName的方法。这样前端能拿到用户的名称。我想的是如果前端传PermissionController和GetPermission等其他不同的struct中不同的方法我都能动态的执行不同的方法,当然如果找不到对应的struct和不同的方法,那肯定是需要告诉前端你请求的方法不存在。下面我们来实现这样的一个功能。
首先定义struct,用一个map来管理struct
//这个是注册好的struct
var registerFunc = map[string]interface{}{
"UserController": &UserController{},
}
type UserController struct {
}
func (u *UserController) GetName(param map[string]string) *ResData {
ret := ResData{}
ret.Code = 10000
ret.Msg = "succ"
if param["id"] == "1" {
ret.Data = "追麾"
}
return &ret
}
type PermissionController struct {
}
func (p *PermissionController) GetPermission() *ResData {
ret := ResData{}
return &ret
}
上面是基础类,下面我们用代码来实现。我直接在代码中进行解释,解析直接看代码的注释。
func main() {
//假如我们传递的参数是params,这里我的value用string了,实际应用中得用interface
params := map[string]string{
"controller": "UserController",
"func": "GetName",
"id": "1",
}
//判断方法是否存在
if _, ok := registerFunc[params["controller"]]; !ok {
fmt.Println("方法不存在")
return
}
c := reflect.ValueOf(registerFunc[params["controller"]])
callback := c.MethodByName(params["func"])
//判断调用的方法是否有效
if !callback.IsValid() {
fmt.Println("方法名不存在")
return
}
//这个方法是通过callback调用,为什么要这么写,因为call方法只支持传递slice,
res := toCombineArr(callback, params)
lastData := ResData{}
//下面这里是通过反射拿调用的方法的返回值。
for _, item := range res {
bin, err := json.Marshal(item.Interface())
if err != nil {
fmt.Println(err)
return
}
err = json.Unmarshal(bin, &lastData)
if err != nil {
fmt.Println(err)
}
break
}
fmt.Println("lastData", lastData)
}
func toCombineArr(callback reflect.Value, params ...interface{}) []reflect.Value {
//把传进来的参数组装成relect.Value,然后调用call方法
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
d := callback.Call(in)
if callback.IsZero() {
fmt.Println("error callback")
return nil
}
return d
}
最后我们整理一下上面的代码:执行结果如下:
lastData {10000 succ 追麾}
下面是整理的代码,我这里再贴一下:
package main
import (
"fmt"
jsoniter "github.com/json-iterator/go"
"reflect"
)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
func main() {
//假如我们传递的参数是params,这里我的value用string了,实际应用中得用interface
params := map[string]string{
"controller": "UserController",
"func": "GetName",
"id": "1",
}
//判断方法是否存在
if _, ok := registerFunc[params["controller"]]; !ok {
fmt.Println("方法不存在")
return
}
c := reflect.ValueOf(registerFunc[params["controller"]])
callback := c.MethodByName(params["func"])
//判断调用的方法是否有效
if !callback.IsValid() {
fmt.Println("方法名不存在")
return
}
//这个方法是通过callback调用,为什么要这么写,因为call方法只支持传递slice,
res := toCombineArr(callback, params)
lastData := ResData{}
//下面这里是通过反射拿调用的方法的返回值。
for _, item := range res {
bin, err := json.Marshal(item.Interface())
if err != nil {
fmt.Println(err)
return
}
err = json.Unmarshal(bin, &lastData)
if err != nil {
fmt.Println(err)
}
break
}
fmt.Println("lastData", lastData)
}
func toCombineArr(callback reflect.Value, params ...interface{}) []reflect.Value {
//把传进来的参数组装成relect.Value,然后调用call方法
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
d := callback.Call(in)
if callback.IsZero() {
fmt.Println("error callback")
return nil
}
return d
}
//这个是注册好的struct
var registerFunc = map[string]interface{}{
"UserController": &UserController{},
}
type UserController struct {
}
func (u *UserController) GetName(param map[string]string) *ResData {
ret := ResData{}
ret.Code = 10000
ret.Msg = "succ"
if param["id"] == "1" {
ret.Data = "追麾"
}
return &ret
}
type PermissionController struct {
}
func (p *PermissionController) GetPermission() *ResData {
ret := ResData{}
return &ret
}
type ResData struct {
Code int32 `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
上面我们通过struct名和方法动态调用,在我的实践中,出现一个问题,假如我定义一个确定好的struct作为返回参数,struct的参数顺序是从上到下返回,但是通过反射之后返回的struct的结果顺序是无序,反射之后会把struct转换成一个map,造成了返回的无序性。具体问题如下。
我改了GetName的方法
func (u *UserController) GetName(param map[string]string) *ResData {
ret := ResData{}
ret.Code = 10000
ret.Msg = "succ"
if param["id"] == "1" {
ret.Data = "追麾"
} else {
tStr := `{"活动id": "sas", "付费": "gdf", "活动号": "66", "A4": "88", "A5": "333", "A6": "ggg", "A7": "ggg"}`
tTmpJson := TmpJson{}
err := json.Unmarshal([]byte(tStr), &tTmpJson)
if err != nil {
fmt.Println("err", err)
return nil
}
fmt.Println("tTmpJson:", tTmpJson)
ret.Data = tTmpJson
}
return &ret
}
增加一个TmpJson的struct结构:
type TmpJson struct {
D活动Id string `json:"活动_id"`
E付费 string `json:"付费"`
G活动号 string `json:"活动号"`
A4 string `json:"A4"`
A5 string `json:"A5"`
A6 string `json:"A6"`
A7 string `json:"A7"`
}
然后请求参数改成如下:
params := map[string]string{
"controller": "UserController",
"func": "GetName",
"id": "2",
}
最后返回结果如下:
tTmpJson:{ gdf 66 88 333 ggg ggg}
lastData {10000 succ map[A4:88 A5:333 A6:ggg A7:ggg 付费:gdf 活动_id: 活动号:66]}
反射给静态语言提供很好的便利,但是其中的某些坑还是很多,好了关于动态调用不同struct的不同的方法就到这里,有兴趣的可以找我交流。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有