Go語言的三方依賴注入(Dependency Injection)套件uber-go/fx
簡單範例。
範例環境
- Go 1.19
- uber-go/fx v1.20.1
安裝
在專案根目錄執行go get go.uber.org/fx@v1
安裝Fx。
範例一
Fx是透過在本身的運行環境中實現依賴注入,(這個「環境」又稱為IoC容器),一開始用fx.New
建立fx.App
物件,然後呼叫fx.App.Run
來運行Fx。
在fx.New
的參數中設定各種物件的建構式和調用函式。
fx.Provider
用來註冊物件的建構式,告訴Fx如何建立依賴類型的實例,這些建構式的參數可能依賴於其他建構式的結果,Fx會根據建構式的注入參數類型自動透過所提供的其他建構式返回結果將實例注入為參數。
fx.Invoke
在Fx執行時會立刻被調用。在fx.Invoke
中去執行一個參數為*A
的函式func(a *A){}
,該函式需要注入*A
的實例,因此Fx會去調用在fx.Provider
註冊的NewA
建構式。
main.go
package main
import (
"fmt"
"go.uber.org/fx"
)
func main() {
fx.New(
fx.Provide(NewA),
fx.Provide(NewB),
fx.Invoke(func(a *A) {
fmt.Println("invoke")
}),
).Run()
}
type A struct {
*B
}
func NewA(b *B) *A {
fmt.Println("create A")
return &A{
B: b,
}
}
type B struct {
}
func NewB() *B {
fmt.Println("create B")
return &B{}
}
測試
執行程式後輸出以下:
[Fx] PROVIDE *main.A <= main.NewA()
[Fx] PROVIDE *main.B <= main.NewB()
[Fx] PROVIDE fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] INVOKE main.main.func1()
create B
[Fx] RUN provide: main.NewB()
create A
[Fx] RUN provide: main.NewA()
invoke
[Fx] RUNNING
範例二
下圖為本範例個物件的依賴關係:
fx.Annotate
用來標注建構式參數,第一個參數為要被標注的建構式;第二個參數以後為要標注的選項。
fx.As
用來標注建構式返回的實例為介面,用來告訴Fx建構函式要注入的介面參數的實例為何。
在fx.Invoke
中去執行一個參數為CartService
的函式func(cartService CartService)
,該函式需要注入CartService
的實例,因此Fx會去調用在fx.Provider
註冊的NewCartServieImpl
建構式。
main.go
package main
import (
"fmt"
"net/http"
"go.uber.org/fx"
)
func main() {
fx.New(
fx.Provide(
fx.Annotate(
NewCartServiceImpl,
fx.As(new(CartService)),
),
fx.Annotate(
NewPaymentServiceImpl,
fx.As(new(PaymentService)),
),
fx.Annotate(
NewShippingServiceImpl,
fx.As(new(ShippingService)),
),
fx.Annotate(
NewOrderRepoImpl,
fx.As(new(OrderRepo)),
),
fx.Annotate(
NewProductRepoImpl,
fx.As(new(ProductRepo)),
),
fx.Annotate(
NewMemberRepoImpl,
fx.As(new(MemberRepo)),
),
NewHttpClient,
),
fx.Invoke(func(cartService CartService) {
cartService.Checkout()
}),
).Run()
}
// shopping cart
type CartService interface {
Checkout()
}
type CartServiceImpl struct {
orderRepo OrderRepo
paymentService PaymentService
shippingService ShippingService
}
func NewCartServiceImpl(
orderRepo OrderRepo,
paymentService PaymentService,
shippingService ShippingService,
) *CartServiceImpl {
return &CartServiceImpl{
orderRepo: orderRepo,
paymentService: paymentService,
shippingService: shippingService,
}
}
func (svc CartServiceImpl) Checkout() {
fmt.Println("checkout - start")
svc.orderRepo.GetOrder()
svc.paymentService.Pay()
svc.shippingService.Ship()
fmt.Println("checkout - end")
}
// payment
type PaymentService interface {
Pay()
}
type PaymentServiceImpl struct {
httpClient *http.Client
}
func NewPaymentServiceImpl(httpClient *http.Client) *PaymentServiceImpl {
return &PaymentServiceImpl{
httpClient: httpClient,
}
}
func (svc PaymentServiceImpl) Pay() {
fmt.Println("make payment")
}
// shipping
type ShippingService interface {
Ship()
}
type ShippingServiceImpl struct {
productRepo ProductRepo
memberRepo MemberRepo
}
func NewShippingServiceImpl(
productService ProductRepo,
memberService MemberRepo,
) *ShippingServiceImpl {
return &ShippingServiceImpl{
productRepo: productService,
memberRepo: memberService,
}
}
func (svc ShippingServiceImpl) Ship() {
fmt.Println("ship - start")
svc.productRepo.GetProduct()
svc.memberRepo.GetMember()
fmt.Println("ship - end")
}
// product
type ProductRepo interface {
GetProduct()
}
type ProductRepoImpl struct {
}
func NewProductRepoImpl() *ProductRepoImpl {
return &ProductRepoImpl{}
}
func (svc ProductRepoImpl) GetProduct() {
fmt.Println("get product")
}
// order
type OrderRepo interface {
GetOrder()
}
type OrderRepoImpl struct {
}
func NewOrderRepoImpl() *OrderRepoImpl {
return &OrderRepoImpl{}
}
func (svc OrderRepoImpl) GetOrder() {
fmt.Println("get order")
}
// member
type MemberRepo interface {
GetMember()
}
type MemberRepoImpl struct {
}
func NewMemberRepoImpl() *MemberRepoImpl {
return &MemberRepoImpl{}
}
func (svc MemberRepoImpl) GetMember() {
fmt.Println("get member")
}
func NewHttpClient() *http.Client {
return http.DefaultClient
}
測試
執行程式輸出以下結果:
[Fx] PROVIDE main.CartService <= fx.Annotate(main.NewCartServiceImpl(), fx.As([[main.CartService]])
[Fx] PROVIDE main.PaymentService <= fx.Annotate(main.NewPaymentServiceImpl(), fx.As([[main.PaymentService]])
[Fx] PROVIDE main.ShippingService <= fx.Annotate(main.NewShippingServiceImpl(), fx.As([[main.ShippingService]])
[Fx] PROVIDE main.OrderRepo <= fx.Annotate(main.NewOrderRepoImpl(), fx.As([[main.OrderRepo]])
[Fx] PROVIDE main.ProductRepo <= fx.Annotate(main.NewProductRepoImpl(), fx.As([[main.ProductRepo]])
[Fx] PROVIDE main.MemberRepo <= fx.Annotate(main.NewMemberRepoImpl(), fx.As([[main.MemberRepo]])
[Fx] PROVIDE *http.Client <= main.NewHttpClient()
[Fx] PROVIDE fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] INVOKE main.main.func1()
[Fx] RUN provide: fx.Annotate(main.NewOrderRepoImpl(), fx.As([[main.OrderRepo]])
[Fx] RUN provide: main.NewHttpClient()
[Fx] RUN provide: fx.Annotate(main.NewPaymentServiceImpl(), fx.As([[main.PaymentService]])
[Fx] RUN provide: fx.Annotate(main.NewProductRepoImpl(), fx.As([[main.ProductRepo]])
[Fx] RUN provide: fx.Annotate(main.NewMemberRepoImpl(), fx.As([[main.MemberRepo]])
[Fx] RUN provide: fx.Annotate(main.NewShippingServiceImpl(), fx.As([[main.ShippingService]])
[Fx] RUN provide: fx.Annotate(main.NewCartServiceImpl(), fx.As([[main.CartService]])
checkout - start
get order
make payment
ship - start
get product
get member
ship - end
checkout - end
[Fx] RUNNING
沒有留言:
張貼留言