Go語言的三方依賴注入(Dependency Injection)套件uber-go/fx
範例。
範例環境
- Go 1.19
- uber-go/fx v1.20.1
安裝
在專案根目錄執行go get go.uber.org/fx@v1
安裝Fx。
事前要求
參考「Golang uber Fx 依賴注入套件 簡單範例」瞭解基本用法。
範例
本範例來自官方文件的範例,只做點小修改,可以直接參考Fx - Get started with Fx。
Fx是透過在本身的運行環境中實現依賴注入,所以一開始必須用fx.New
建立fx.App
物件,然後呼叫fx.App.Run
來運行Fx。
在fx.New
的參數中設定各種物件的建構式和調用函式。
fx.Provider
用來註冊物件的建構式,告訴Fx如何建立依賴類型的實例,這些建構式的參數可能依賴於其他建構式的結果。例如NewHTTPServer
的參數fx.Lifecycle
實例由Fx本身提供,而*http.ServeMux
的實例是由NewServeMux
提供。
同樣地,NewServeMux
的參數[]Route
分別由實現Route
介面的EchoHandler
的建構式NewEchoHandler
和HelloHandler
的建構式NewHelloHandler
提供。
fx.Annotate
用來標注建構式參數,第一個參數為要被標注的建構式;第二個參數以後為要標注的選項。
fx.ParamTags
用來標注建構式參數實例的名稱(`name:"..."`
)、群組(`group:"..."`
)或選填(`optional:"true"`
)等,用來和建構式返回類型對應。fx.ResultTags
用來標注建構式返回實例的名稱(`name:"..."`
)、群組(`group:"..."`
)等,用來和建構式參數類型對應。fx.As
用來標注建構式返回的實例為介面。
fx.Invoke
在Fx執行時會立刻被調用,若有多個會依設定順序執行。
fx.Provider
註冊的建構式順序不重要,且只有需要用到返回的類型時才會被調用,而為了讓在fx.Provide
中註冊的NewHTTPServer
建構式被調用,所以在fx.Invoke
中去執行一個參數為*http.Server
的空函式func(*http.Server) {}
。也就是說func(*http.Server) {}
需要注入*http.Server
的實例,因此Fx才會去調用在fx.Provider
註冊的NewHTTPServer
建構式。
main.go
package main
import (
"context"
"fmt"
"net"
"net/http"
"go.uber.org/fx"
)
func main() {
fx.New(
fx.Provide(
NewHTTPServer,
fx.Annotate(
NewServeMux,
fx.ParamTags(`group:"routes"`),
),
AsRoute(NewEchoHandler),
AsRoute(NewHelloHandler),
),
fx.Invoke(func(*http.Server) {}),
).Run()
}
func AsRoute(f any) any {
return fx.Annotate(
f,
fx.As(new(Route)),
fx.ResultTags(`group:"routes"`),
)
}
func NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux) *http.Server {
srv := &http.Server{Addr: ":8080", Handler: mux}
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
ln, err := net.Listen("tcp", srv.Addr)
if err != nil {
return err
}
fmt.Println("Starting HTTP server at", srv.Addr)
go srv.Serve(ln)
return nil
},
OnStop: func(ctx context.Context) error {
return srv.Shutdown(ctx)
},
})
return srv
}
type Route interface {
http.Handler
Pattern() string
}
type EchoHandler struct{}
func NewEchoHandler() *EchoHandler {
return &EchoHandler{}
}
func (h *EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("echo"))
}
func (h *EchoHandler) Pattern() string {
return "/echo"
}
type HelloHandler struct{}
func NewHelloHandler() *HelloHandler {
return &HelloHandler{}
}
func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello"))
}
func (h *HelloHandler) Pattern() string {
return "/hello"
}
func NewServeMux(routes []Route) *http.ServeMux {
mux := http.NewServeMux()
for _, route := range routes {
mux.Handle(route.Pattern(), route)
}
return mux
}
沒有留言:
張貼留言