Go Revel - main函数分析

运行revel命令时,首先会编译整个项目,在编译时,会根据app.conf配置文件生成两个源码文件tmp/main.goroutes/routes.go,其中main.go是整个项目的入口。

main.go与routes.go源码生成过程

golang_jsonrpc_server

源码在revel的revel/harness包。 https://github.com/robfig/revel/blob/master/harness/build.go

main.go的生成比较重要,而routes.go源码则是完全根据conf/routes配置文件的规则生成。


main.go分析

这里以booking示例项目为例子。

包的导入

模板:

import (
    "flag"
    "reflect"
    "github.com/robfig/revel"{{range $k, $v := $.ImportPaths}}
    {{$v}} "{{$k}}"{{end}}
)

渲染后:

import (
    "flag"
    "reflect"
    "github.com/robfig/revel"
    _ "booking/app"
    controllers "booking/app/controllers"
    _ "booking/app/jobs"
    tests "booking/tests"
    _ "github.com/mattn/go-sqlite3"
    controllers0 "github.com/robfig/revel/modules/jobs/app/controllers"
    _ "github.com/robfig/revel/modules/jobs/app/jobs"
    controllers2 "github.com/robfig/revel/modules/static/app/controllers"
    _ "github.com/robfig/revel/modules/testrunner/app"
    controllers1 "github.com/robfig/revel/modules/testrunner/app/controllers"
    models "github.com/robfig/revel/samples/booking/app/models"
)

这里动态的渲染出需要导入的包,必要时使用别名,导入包由calcImportAliases方法生成。

注册控制器

mian中进行控制器(controller)的注册

模板:

{{range $i, $c := .Controllers}}
    revel.RegisterController((*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil),
        []*revel.MethodType{
            {{range .MethodSpecs}}&revel.MethodType{
                Name: "{{.Name}}",
                Args: []*revel.MethodArg{ {{range .Args}}
                    &revel.MethodArg{Name: "{{.Name}}", Type: reflect.TypeOf((*{{index $.ImportPaths .ImportPath | .TypeExpr.TypeName}})(nil)) },{{end}}
                },
                RenderArgNames: map[int][]string{ {{range .RenderCalls}}
                    {{.Line}}: []string{ {{range .Names}}
                        "{{.}}",{{end}}
                    },{{end}}
                },
        },
        {{end}}
    })
{{end}}

渲染后:

...
revel.RegisterController((*controllers.Hotels)(nil),
    []*revel.MethodType{
        &revel.MethodType{
            Name: "Index",
            Args: []*revel.MethodArg{ 
            },
            RenderArgNames: map[int][]string{ 
                37: []string{ 
                    "bookings",
                },
            },
        },
        &revel.MethodType{
            Name: "List",
            Args: []*revel.MethodArg{ 
                &revel.MethodArg{Name: "search", Type: reflect.TypeOf((*string)(nil)) },
                &revel.MethodArg{Name: "size", Type: reflect.TypeOf((*int)(nil)) },
                &revel.MethodArg{Name: "page", Type: reflect.TypeOf((*int)(nil)) },
            },
            RenderArgNames: map[int][]string{ 
                58: []string{ 
                    "hotels",
                    "search",
                    "size",
                    "page",
                    "nextPage",
                },
            },
        },
        &revel.MethodType{
            Name: "Show",
            Args: []*revel.MethodArg{ 
                &revel.MethodArg{Name: "id", Type: reflect.TypeOf((*int)(nil)) },
            },
            RenderArgNames: map[int][]string{ 
                89: []string{ 
                    "title",
                    "hotel",
                },
            },
        },
        &revel.MethodType{
            Name: "Settings",
            Args: []*revel.MethodArg{ 
            },
            RenderArgNames: map[int][]string{ 
                93: []string{ 
                },
            },
        },
        &revel.MethodType{
            Name: "SaveSettings",
            Args: []*revel.MethodArg{ 
                &revel.MethodArg{Name: "password", Type: reflect.TypeOf((*string)(nil)) },
                &revel.MethodArg{Name: "verifyPassword", Type: reflect.TypeOf((*string)(nil)) },
            },
            RenderArgNames: map[int][]string{ 
            },
        },
        &revel.MethodType{
            Name: "ConfirmBooking",
            Args: []*revel.MethodArg{ 
                &revel.MethodArg{Name: "id", Type: reflect.TypeOf((*int)(nil)) },
                &revel.MethodArg{Name: "booking", Type: reflect.TypeOf((*models.Booking)(nil)) },
            },
            RenderArgNames: map[int][]string{ 
                144: []string{ 
                    "title",
                    "hotel",
                    "booking",
                },
            },
        },
        &revel.MethodType{
            Name: "CancelBooking",
            Args: []*revel.MethodArg{ 
                &revel.MethodArg{Name: "id", Type: reflect.TypeOf((*int)(nil)) },
            },
            RenderArgNames: map[int][]string{ 
            },
        },
        &revel.MethodType{
            Name: "Book",
            Args: []*revel.MethodArg{ 
                &revel.MethodArg{Name: "id", Type: reflect.TypeOf((*int)(nil)) },
            },
            RenderArgNames: map[int][]string{ 
                163: []string{ 
                    "title",
                    "hotel",
                },
            },
        },

    })
...

RegisterController接受两个参数,(c interface{}, methods []*MethodType),代码中,第一个参数传入(*controllers.Hotels)(nil),其实是一个controllers.Hotels类型的空指针,方法内部会根据这个类型指针获取这个controller的结构名;第二个参数为这个controller所有Action的集合 []*MethodType

每一个MethodType对应一个Action, Action即符合绑定于controller并且暴露出来而且返回值为revel.Result的方法,参数不限。

例如:

// Action
&revel.MethodType{
    // Action的名称,即方法名
    Name: "List",

    // Action所接受的参数,即方法的参数
    Args: []*revel.MethodArg{
        // 每个参数的变量名称,以及反射类型
        &revel.MethodArg{Name: "search", Type: reflect.TypeOf((*string)(nil)) },
        &revel.MethodArg{Name: "size", Type: reflect.TypeOf((*int)(nil)) },
        &revel.MethodArg{Name: "page", Type: reflect.TypeOf((*int)(nil)) },
    },

    // 返回Result时调用Render来渲染模板的参数名
    RenderArgNames: map[int][]string{

        // 这里获取了调用Render时源码中的行号,行号是在异常时显示出来方便调试定位(感觉是这样)
        58: []string{
            "hotels",
            "search",
            "size",
            "page",
            "nextPage",
        },
    },
},

注册验器

模板:

revel.DefaultValidationKeys = map[string]map[int]string{ {{range $path, $lines := .ValidationKeys}}
    "{{$path}}": { {{range $line, $key := $lines}}
        {{$line}}: "{{$key}}",{{end}}
    },{{end}}
}

渲染后:

revel.DefaultValidationKeys = map[string]map[int]string{
    "booking/app/controllers.Application.SaveUser": {
        55: "verifyPassword",
        56: "verifyPassword",
    },
    "booking/app/controllers.Hotels.SaveSettings": {
        98: "verifyPassword",
        100: "verifyPassword",
    },
    "booking/app/models.(*Hotel).Validate": {
        19: "hotel.Name",
        21: "hotel.Address",
        26: "hotel.City",
        32: "hotel.State",
        38: "hotel.Zip",
        44: "hotel.Country",
    },
    "booking/app/models.(*User).Validate": {
        28: "user.Username",
        36: "user.Name",
    },
    "booking/app/models.Booking.Validate": {
        34: "booking.User",
        35: "booking.Hotel",
        36: "booking.CheckInDate",
        37: "booking.CheckOutDate",
        39: "booking.CardNumber",
        41: "booking.NameOnCard",
    },
    "booking/app/models.ValidatePassword": {
        44: "password",
    },
}

这里注册了所有的验证器,并且标记了所有调用验证器Validation方法的地方,包括行号以及传入的变量名。

注册测试用例

模板:

revel.TestSuites = []interface{}{ {{range .TestSuites}}
    (*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil),{{end}}
}

渲染后:

revel.TestSuites = []interface{}{ 
    (*tests.ApplicationTest)(nil),
}

这里仅注册当前项目的测试用例。

最后一行,revel.Run(*port) 开启服务器监听,运行server。

posted on   黑暗伯爵  阅读(2079)  评论(0编辑  收藏  举报

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示