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
}
沒有留言:
張貼留言