Go Revel - main函数分析
运行revel命令时,首先会编译整个项目,在编译时,会根据`app.conf`配置文件生成两个源码文件`tmp/main.go`、`routes/routes.go`,其中`main.go`是整个项目的入口。
## main.go与routes.go源码生成过程
![golang_jsonrpc_server](http://images.cnblogs.com/cnblogs_com/hangxin1940/508415/o_golang-revel-build.png "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。