Go Revel - main函数分析
运行revel命令时,首先会编译整个项目,在编译时,会根据`app.conf`配置文件生成两个源码文件`tmp/main.go`、`routes/routes.go`,其中`main.go`是整个项目的入口。
## main.go与routes.go源码生成过程

源码在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。
Go Revel - main函数分析的更多相关文章
- RT-thread main函数分析
RT-thread系统的main函数位于startup.c文件中. /** * This function will startup RT-Thread RTOS. */ void rtthread_ ...
- Tomcat启动分析(一)-从脚本到main函数分析
当我们在Linux下启动tomcat的时候,通过ps查看其进程信息为,接下来的内容我们就以此进行分析: [tomcat@fdd ~]$ ps -ef |grep java tomcat : tty1 ...
- SequoiaDB 系列之五 :源码分析之main函数
好久好久没有写博客了,因为一直要做各种事,工作上的,生活上的,这一下就是半年. 时光如梭. 这两天回头看了看写的博客,感觉都是贻笑大方. 但是还是想坚持把SequoiaDB系列写完. 初步的打算已经确 ...
- Atitit main函数的ast分析 数组参数调用的ast astview解析
Atitit main函数的ast分析 数组参数调用的ast astview解析 1.1. Xxcls.main(new String[]{"","bb"}) ...
- 性能测试分享: Jmeter的源码分析main函数参数
性能测试分享: Jmeter的源码分析main函数参数 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大 ...
- 大数据学习之Scala中main函数的分析以及基本规则(2)
一.main函数的分析 首先来看我们在上一节最后看到的这个程序,我们先来简单的分析一下.有助于后面的学习 object HelloScala { def main(args: Array[String ...
- Keil开发的ARM程序main函数之前的汇编分析
Keil开发的ARM程序main函数之前的汇编分析 ——BIN文件中RW段的数据移动 系统平台: STM32系列STM32F103ZE,512KB内部FLASH,64KB片内存储; FLASH地址范围 ...
- 魔兽世界服务器Trinitycore分析二:auth server的main函数
TrinityCore由生成两个运行文件authserver和world server以及一堆DLL(或so)文件的子项目组成(先忽略map_extractor等几个工具项目). authserver ...
- ffmpeg源码分析二:main函数和transcode函数 (转2)
原帖地址:http://blog.csdn.net/austinblog/article/details/24804455 首先从main函数看起,关键解释部分已加注释,该函数在ffmpeg.c文件中 ...
随机推荐
- php中array_merge合并数组详解
如果键名有重复,该键的键值为最后一个键名对应的值(后面的覆盖前面的).如果数组是数字索引的,则键名会以连续方式重新索引. 注释:如果仅仅向 array_merge() 函数输入了一个数组,且键名是整数 ...
- dd-wrt 中继配置
本配置方法在tp-link 703n v1.6上应用成功 1.首先把703n刷成dd-wrt.这里我刷的是 DD-WRT v24-sp2 (03/15/12) std版本,要刷两个固件,一个facto ...
- 用Entityframework 调用Mysql时,datetime格式插入不进去数据库的解决办法。
1. 打开Model.edmx, 2. 选择userinfo中的createtime字段的属性 3. storegeneratedpattern设置值为None
- JDK1.6新特性,基础类库篇,集合框架(Collections)
2006 年底,Sun 公司发布了 Java Standard Edition 6(Java SE 6)的最终正式版,代号 Mustang(野马). 集合框架增强如下: 1. 增加了如下新接口(Int ...
- mysql 数据类型TIMESTAMP用法
在mysql数据库中,timestamp数据类型是一个比较特殊的数据类型,可以自动在不使用程序更新情况下只要更新了记录timestamp会自动更新时间. 通常表中会有一个Create date 创建日 ...
- Tsk4.5异步
public async void LoadData<T>(WhereClip where,OrderByClip order) where T : Entity, new() { try ...
- C#中自定义属性的例子
自定义属性的作用 有时候我们需要给一个类或者类中的成员加上一些属性或者附加信息,让类或者变量的功能更明确可控制的细粒度更高,打个简单的比方:数据库里面的一张表,表中的每一个字段都有很多属性,如是否主键 ...
- VLAN,GRE,VXLAN
https://www.cnblogs.com/charles1ee/p/6629009.html VLAN介绍 局域网LAN(多个机器连接一个Hub或者Switch),是一个广播域 VLAN:一台S ...
- linux命令(38):split 分割文件
在Linux下用split进行文件分割: 模式一:指定分割后文件行数 对与txt文本文件,可以通过指定分割后文件的行数来进行文件分割. 命令: split -l 300 large_file.txt ...
- iOS开发transform的使用
// // ViewController.m // 18-transform的使用 #import "ViewController.h" @interface ViewCont ...