本文以最简单的使用方式,来阅读beego源码,探索beego的底层实现细节,一窥其庐山真面目,
需要两部分基础,一是golang的net/http server 的基础,二是反射reflect基础,
net/http server 的基础用于了解beego的底层http服务器的实现,
reflect基础用于了解beego注册控制器和url映射调用方法后,beego底层是如何进行调用的。
router.go文件
package routers
import (
"my_beego_project/controllers"
"github.com/astaxie/beego"
)
func init() {
beego.Router("/", &controllers.MainController{},"POST:PostTest")
}
controller.go文件
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (c *MainController) PostTest() {
c.Data["Website"] = "beego.me"
c.Data["Email"] = "[email protected]"
c.ServeJSON()
}
main.go文件
package main
import (
_ "my_beego_project/routers"
"github.com/astaxie/beego"
)
func main() {
beego.Run()
}
超级简单,没有多余的代码。
1.首先看main.go的beego.Run()
func Run(params ...string) {
initBeforeHTTPRun()
if len(params) > 0 && params[0] != "" {
strs := strings.Split(params[0], ":")
if len(strs) > 0 && strs[0] != "" {
BConfig.Listen.HTTPAddr = strs[0]
}
if len(strs) > 1 && strs[1] != "" {
BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
}
BConfig.Listen.Domains = params
}
BeeApp.Run()
}
可见,此函数也很简单,解析传入的可变参数params,如果有就解析到全局配置的监听的地址和端口,以及域名中
然后执行BeeApp.Run(),咱们继续看BeeApp.Run(),这里BeeApp是一个对象,Run是该对象的方法,暂时咱们先不看,继续看方法
2.BeeApp.Run()
func (app *App) Run(mws ...MiddleWare) {
...
app.Server.Handler = app.Handlers
...
if BConfig.Listen.EnableHTTP {
go func() {
app.Server.Addr = addr
logs.Info("http server Running on http://%s", app.Server.Addr)
if BConfig.Listen.ListenTCP4 {
...
} else {
if err := app.Server.ListenAndServe(); err != nil {
logs.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}
}()
}
}
其他不是本章主线的代码,我已经注释掉了,
现在主要可观察到,http server是采用beego自定义的server,使用 app.Server.ListenAndServe() 开启服务,并且为该Server设置了监听地址
以及自定义的Handler,这个Handler是BeeApp结构体本身的,那么这个Handler是什么呢?这个BeeApp结构体又是什么?那么我们继续看代码
3.BeeApp结构
func init() {
// create beego application
BeeApp = NewApp()
}
// App defines beego application with a new PatternServeMux.
type App struct {
Handlers *ControllerRegister
Server *http.Server
}
// NewApp returns a new beego application.
func NewApp() *App {
cr := NewControllerRegister()
app := &App{Handlers: cr, Server: &http.Server{}}
return app
}
很好,BeeApp是一个App结构体的对象,这个结构体有两个成员,BeeApp是一个全局对象,通过NewApp直接在代码初始化时就分配了。
这个对象的Server在初始化时直接分配的一个空结构体,看来里面的参数应该是在后面逐个填进去的。不过这不是本章重点,
主要看看这个控制器Handlers,http server的Handler都是继承net/http包下的Handler接口,该接口有一个ServeHTTP方法,这方法才是http server的核心,
那么我们看这个Beego的Handler,主要看看这个结构的ServeHTTP方法是在干啥。
3.NewControllerRegister
type ControllerRegister struct {
routers map[string]*Tree
enablePolicy bool
policies map[string]*Tree
enableFilter bool
filters [FinishRouter + 1][]*FilterRouter
pool sync.Pool
}
// NewControllerRegister returns a new ControllerRegister.
func NewControllerRegister() *ControllerRegister {
return &ControllerRegister{
routers: make(map[string]*Tree),
policies: make(map[string]*Tree),
pool: sync.Pool{
New: func() interface{} {
return beecontext.NewContext()
},
},
}
}
可见,该函数主要就是构建一个ControllerRegister 对象,该对象有几个成员,全都不对外开放,其中有个routers,重点关注,可能就是跟我们注册控制器,
beego服务器 根据url映射到具体方法的功能有关。
不过现在我们看看ControllerRegister结构的ServeHTTP方法,看看具体怎么处理http请求。在哪里做的路由
看代码:
func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
...
routerInfo, findRouter = p.FindRouter(context)
...
if routerInfo.routerType == routerTypeRESTFul {
...
}else if routerInfo.routerType == routerTypeHandler {
...
}else {
runMethod = method
}
...
vc := reflect.ValueOf(execController)
method := vc.MethodByName(runMethod)
in := param.ConvertParams(methodParams, method.Type(), context)
out := method.Call(in)
...
}
同样,省略掉了无关代码,可以看到,ControllerRegister作为自定义的Handler,所有http请求都将经过ServeHTTP方法,在里面进行路由,调用对应的方法进行逻辑处理。
这里通过反射的方式调用到具体的方法。那么这里竟然还有参数传递进去,参数是啥呢?经过我的源码追踪,发现是Nil。也就是不会存在参数,那就奇怪了。
源码分析二:controller注册器注册路由
func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {
BeeApp.Handlers.Add(rootpath, c, mappingMethods...)
return BeeApp
}
继续