Go反射-通过反射调用结构体的方法(带入参)

使用反射前,我们需要提前做好映射配置

papckage_struct_relationship.go

go 复制代码
package reflectcommon

import (
	api "template/api"
)

// 包名到包对象的映射
var structMap = map[string]func() interface{}{
	"template/api": func() interface{} { return &api.User{} },   // 调用User结构体的方法
}

反射核心代码

go 复制代码
package reflectcommon

import (
	"fmt"
	"reflect"
	api "template/api"
)

/*
args []interface{},支持任意类型的参数
*/
func CallPackageStructMethod(pkgPath, methodName string, args []interface{}) ([]reflect.Value, *[]error) {
	fmt.Println(123123)
	errs := []error{}

	// 1. 获取构造函数
	constructor, ok := structMap[pkgPath]
	if !ok {
		errs = append(errs, fmt.Errorf("包 %s 未注册", pkgPath))
		return nil, &errs
	}

	// 2. 创建实例
	instance := constructor()
	instanceValue := reflect.ValueOf(instance)

	// 3. 检查是否为指针
	if instanceValue.Kind() != reflect.Ptr {
		errs = append(errs, fmt.Errorf("构造函数必须返回指针"))
		return nil, &errs
	}

	// 4. 获取指向的结构体值
	structValue := instanceValue.Elem()
	if structValue.Kind() != reflect.Struct {
		errs = append(errs, fmt.Errorf("预期结构体,实际得到 %s", structValue.Kind()))
		return nil, &errs
	}
	
	// 5. 获取方法
	method := instanceValue.MethodByName(methodName)
	if !method.IsValid() {
		errs = append(errs, fmt.Errorf("方法 %s 不存在", methodName))
		return nil, &errs
	}

	// 6. 准备参数
	var in []reflect.Value
	for _, arg := range args {
		in = append(in, reflect.ValueOf(arg))
	}

	// 7. 调用方法并返回结果
	results := method.Call(in)
	return results, nil
}

定义结构体方法

template/api/create_token.go

go 复制代码
package api

import (
	"fmt"
	"template/common"
	"template/types"
)

type User struct {
	UserId string
	Token  string
}

func newCreateTokenApiV1() *types.Api {
	apiV1 := types.Api{
		Host:        "",
		Prefix:      "",
		Version:     "/v1",
		Url:         "/token/create",
		Method:      "POST",
		ContentType: "application/json",
	}
	return &apiV1
}


func (user User) CreateTokenApi(user1 map[string]interface{}, userId, token string) (*types.Api, *types.Result, error) {
	// 处理请求URL
	data := map[string]interface{}{
		"userId": userId,
		"token":  token,
		"user":   user1,
	}
	fmt.Println("xxxxxxx---进来啦")
	apiV1 := newCreateTokenApiV1()
	apiV1.ReqData = data

	result := &types.Result{
		Header:   nil,
		Duration: 2,
		Resp: struct {
			Code      string      `json:"code"`
			Message   string      `json:"message"`
			RequestId string      `json:"requestId"`
			Response  interface{} `json:"response"`
		}{
			Code:      "000000",
			Message:   "success",
			RequestId: "123456",
			Response:  data,
		},
	}
	return apiV1, result, nil
}

调用

go 复制代码
package main

import (
	"template/common"
	reflectcommon "template/reflectCommon"
	"template/types"

	"github.com/douyu/jupiter/pkg/xlog"
)

func main() {
	// 这里的顺序主要要与调用发放的入参保持一致
	reqData := []interface{}{
		map[string]interface{}{"id": 1, "name": "test", "age": 18},
		"MyUserid",
		"MuToken",
	} }}
	results, errList := reflectcommon.CallPackageStructMethod("template/api", "CreateTokenApi", reqData)
	if errList != nil && len(*errList) > 0 {
		xlog.Error("CreateTokenApi failed", xlog.Any("error", errList))
		return
	}

	// 5. 解析返回结果 (api, result, error)
	if len(results) >= 3 {
		apiV1 := results[0].Interface().(*types.Api)
		result := results[1].Interface().(*types.Result)
		err := results[2].Interface() // 可能是nil

		xlog.Info("success",
			xlog.Any("api", apiV1),
			xlog.Any("result", result),
			xlog.Any("error", err))
	}

}

执行日志:

PS D:\project\go\src\template> go run .

123123

xxxxxxx---进来啦

1746258031 INFO default success {"api": {"name":"","prefix":"/efile","version":"/v1","url":"/token/create","fullUrl":"","method":"POST","reqHeader":null,"ContentType":"application/json","param":null,"reqData":{"token":"MuToken","user":{"age":18,"id":1,"name":"test"},"userId":"MyUserid"},"excuteUid":"","respContentType":""}, "result": {"header":null,"duration":2,"resp":{"code":"000000","message":"success","requestId":"123456","response":{"token":"MuToken","user":{"age":18,"id":1,"name":"test"},"userId":"MyUserid"}}}, "error": null}

相关推荐
八股文领域大手子4 分钟前
单机 vs 分布式:Java 后端限流的选择题
java·开发语言·数据结构·算法·spring
钢铁男儿1 小时前
C# 类成员的访问:内部与外部
服务器·开发语言·c#
薛慕昭2 小时前
《ESP32无线网络编程全攻略:从STA/AP模式到NTP时间同步》
开发语言·单片机·嵌入式硬件
小杨升级打怪中2 小时前
前端面经-VUE3篇(二)--vue3组件知识(二)依赖注入、异步组件、生命周期、组合式函数、插件
开发语言·前端·javascript
催眠大树3 小时前
适配器模式(Adapter Pattern)
java·开发语言·适配器模式
博哥爱学习4 小时前
《Java高级编程:从原理到实战 - 进阶知识篇四》
java·开发语言
小徐Chao努力4 小时前
【堆】最大堆、最小堆以及GO语言的实现
数据结构·算法·golang·
jiunian_cn5 小时前
【c++】模板详解
开发语言·c++·visual studio
工藤新一¹6 小时前
C++/SDL 进阶游戏开发 —— 双人塔防(代号:村庄保卫战 19)
开发语言·c++·游戏引擎·游戏开发·sdl