由于revel框架本身對于model層的編寫沒有提供任何指導,所以在設計這部分的時候就有些猶豫,反復斟酌到底怎樣才算是最佳實踐。
我在做山坡網的時候剛開始也糾結了一下,拿不準mongodb的session的創建和銷毀應該在什么地方處理。直到有一天看到了revmgo的作者在與revel的作者討論(具體內容在這里),就去研究了下revmgo,之后立即就用它替換了我自己的實現。
先說下用法吧。
1. 在app.conf添加mongodb的連接字符串。
revmgo.dial = mongodb://username:password@serverip/database
2. 在controller層的init方法中初始化。
func init() {
revmgo.ControllerInit()revel.OnAppStart(func() {
revmgo.AppInit()
}
}
3. 把MongoController簽入到所有需要訪問數據庫的Controller中。
type Application struct {
*revel.Controller
revmgo.MongoController
}
這里有個小插曲,revmgo.MongoController已經內嵌過revel.Controller了,但此處還是不能省略*revel.Controller。原因是revel在確定有效的controller的action的時候只反射了一層,跟revel的作者討論過這個問題,后來他說服我了,原因是如果不斷遞歸反射的話復雜度會變高而且性能損失會不可控。好吧,其實這也算是OO的遺毒。看起來挺難受,多寫了一行代碼,設計上也不太舒服,但仔細想來對性能和整體的復雜度卻都有好處,既然如此,何必執著設計上的完美呢?簡單就是美。
4. 修改Dal的實現。
//Data access layer
type Dal struct {
session *mgo.Session
}//創建新的Dal
func NewDal(session *mgo.Session) *Dal {
if session == nil {
panic("session cannot be nil.")
}
return &Dal{session}
}
現在來看看在controller中如何使用。
func (c *Account) HandleRegister(user *models.MockUser) revel.Result {
//Validate parameter here.
dal := models.NewDal(c.MongoSession)
err := dal.RegisterUser(user)
//Biz logic here
}
revmgo的內部實現非常簡單,整個代碼也就110行,基本可以一目了然。
總的來說,調用revmgo.AppInit()的時候讀取app.conf中的配置,可以看到有兩個配置項有效,"revmgo.dial"為連接字符串,"db.spec"確定mongodb的session的重復利用方式(具體含義參考這里),然后建立session。
之后的revmgo.ControllerInit注冊了兩個interceptfunc,用于在controller的action訪問前后建立和關閉session。
func ControllerInit() {
revel.InterceptMethod((*MongoController).Begin, revel.BEFORE)
revel.InterceptMethod((*MongoController).End, revel.FINALLY)
}
其實我對于這個設計頗有微詞,ControllerInit的存在簡直就是對revel的module設計機制的嘲笑,很難相信作為一個模塊竟然還需要使用者手動調用初始化函數。這個部分應該不難處理,常規來說只應該讓使用者注冊需要使用的module和執行順序,初始化和解構都由module管理器負責。跟revel的作者也討論了這個問題,看他似乎有別的設計意圖,等等看revel 1.0正式發布時會是什么樣子吧。
文章列表