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/likecs_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。