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文件中 ...
随机推荐
- Shell脚本:向磁盘中批量写入数据
一.关于本文 工作要做的监控系统需要监控磁盘空间的使用率并报警.在测试这个功能的时候需要模拟两个场景:一是磁盘空间不断增长超过设定的阈值时,需要触发报警机制:二是磁盘空间降落到低于报警阈值的时候,不再 ...
- [转]Intellij IDEA快捷键与使用小技巧
Ctrl+Shift + Enter,语句完成“!”,否定完成,输入表达式时按 “!”键Ctrl+E,最近的文件Ctrl+Shift+E,最近更改的文件Shift+Click,可以关闭文件Ctrl+[ ...
- Centos 7搭建Gitlab服务器超详细(转)
一. 安装并配置必要的依赖关系 在CentOS系统上安装所需的依赖:ssh,防火墙,postfix(用于邮件通知) ,wget,以下这些命令也会打开系统防火墙中的HTTP和SSH端口访问. 1.安装s ...
- mysql 8.0 java连接报错:Unknown system variable 'query_cache_size'
java连接mysql 8.0.11报错 java.sql.SQLException: Unknown system variable 'query_cache_size' at com.mysql. ...
- Create rolling monthly, weekly and daily Logstash indices
在刚刚开始接触ELK的时候我们习惯把每一个index都按照day来切割.但是我们会发现我们的shards 会很多. 其实我们一该把那些小的index按照一周或者一个月来rolling,来减少我们的sh ...
- [na]交换机原理/macof
交换机的工作原理 简单来说,就是根据源mac学习-->形成cam表,根据cam表转发. 正常情况下先arp广播,sw收到后发到本vlan所有出口,所有机器学习更新arp缓存. 目标机返回单播ar ...
- PS辅助工具Assistor PS
Assistor PS是一个功能强大的PS辅助工具,它可以切图.标坐标.尺寸.文字样式注释.画参考线等功能,可以为设计师节省很多时间.该PS工具原本需要每月付费$9.99美元,但在今年6月开始,将免费 ...
- JVM 类加载机制详解
如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lan ...
- .NET MVC5+ Dapper+扩展+微软Unity依赖注入实例
1.dapper和dapper扩展需要在线安装或者引用DLL即可 使用nuget为项目增加Unity相关的包 2.model类 public class UserInfo { public int I ...
- 微信小程序启动过程分析
1.微信客户端在打开小程序之前,会把整个小程序的代码包下载到本地. 2.紧接着通过 app.json 的 pages 字段就可以知道你当前小程序的所有页面路径: { "pages" ...