反射入门可以先看这个http://colobu.com/2015/09/23/laws-of-goang-reflection/
因为在使用gin框架,在配置路由的时候需要手动配置,如下:
r.GET("/page",controller.Page)
r.GET("/index",controller.Index)
其中controller是包名,如果在controller包中再增加方法,那就需要在这个地方继续更新包,所以考虑有没有方法自动扫描controller中的包,进行自动注册呢。
于是想到了反射,但是查阅资料没有发现可以用反射来提取包中的方法。只能另寻思路。
新的思路是,新建一个类,每个控制方法都属于这个类。
如下:
type Ctl struct{
}
那么这个时候,就可以通过Ctl这个类对象来获取其所有的方法,这样就可以根据方法来设置路由规则,达到自动扫描功能。
其中controller这样写:
func (this *Ctl) GETPage(c *gin.Context){
c.HTML(http.StatusOK, "page/page.tmpl",gin.H{
"title":"TEST",
})
}
反射中这样写的:
ctl := controller.Ctl{}
// 通过反射获取ctl中的所有方法
refCtl := reflect.TypeOf(&ctl)
methodCnt := refCtl.NumMethod()
for index := 0; index<methodCnt; index++{
m := refCtl.Method(index)
methodName := m.Name
if strings.HasPrefix(methodName,"GET") {
name := strings.ToLower(strings.Replace(methodName,"GET","",1))
fmt.Println(name)
r.GET(name,m.Func.Interface().(gin.HandlerFunc))
//r.GET(name,m.Func)
}
}
然而最终给我报错,
interface {} is func(*controller.Ctl, *gin.Context), not gin.HandlerFunc
到这我明白了,形如func (this *Ctl) GETPage(c *gin.Context)
的函数,其底层其实是func GETPage(this *Ctl, c *gin.Context)
但是虽然如此,这个问题依然没难倒我(用了一下午想出来一个办法。。)
方法是使用map保存path和方法的映射,router中设置统一入口,在该入口进行路由配置,代码如下:
package router
import (
"github.com/gin-gonic/gin"
"blog/controller"
"reflect"
"strings"
)
var (
ctl = controller.Ctl{}
methods = make(map[string]reflect.Method)
)
func SetRouter(r *gin.Engine){
// 通过反射获取ctl中的所有方法
refCtl := reflect.TypeOf(&ctl)
methodCnt := refCtl.NumMethod()
for index := 0; index<methodCnt; index++{
m := refCtl.Method(index)
methodName := m.Name
if strings.HasPrefix(methodName,"GET") {
name := strings.ToLower(strings.Replace(methodName,"GET","",1))
//将path对应的反射方法保存
methods[name] = m
// 设置路由统一入口
r.GET(name,doHandle)
}
}
}
//统一入口
func doHandle(ctx *gin.Context){
//获取path
p := strings.Split(ctx.Request.URL.Path,"/")[1]
vals := make([]reflect.Value,2)
vals[0] = reflect.ValueOf(&ctl)
vals[1] = reflect.ValueOf(ctx)
//反射进行调用
methods[p].Func.Call(vals)
}
当然这只是提供一种思路,实际使用的时候,因为路由的配置相当复杂,还是建议手动进行配置