淺析golang的依賴注入
前言
如果是做web開發(fā),對(duì)依賴注入肯定不陌生,java程序員早就習(xí)慣了spring提供的依賴注入,做業(yè)務(wù)開發(fā)時(shí)非常方便,只關(guān)注業(yè)務(wù)邏輯即可,對(duì)象之間的依賴關(guān)系都交給框架。
golang是強(qiáng)類型語(yǔ)言,編譯后是機(jī)器碼,所以一般使用 反射 或 代碼生成 解決依賴注入的問題
基于反射的DI
基于反射解決DI問題的框架, 使用比較多的是Uber的 dig 庫(kù)
官方的例子:
type Config struct {
Prefix string
}
//初始化Config函數(shù)
func NewConfig()(*Config, error) {
// In a real program, the configuration will probably be read from a
// file.
var cfg Config
err := json.Unmarshal([]byte(`{"prefix": "[foo] "}`), &cfg)
return &cfg, err
}
//初始化logger函數(shù)
func NewLogger(cfg *Config) *log.Logger {
return log.New(os.Stdout, cfg.Prefix, 0)
}
func Handle() (l *log.Logger) {
l.Print("You've been invoked")
}
func main() {
//初始化dig對(duì)象
c := dig.New()
//Provide方法用來設(shè)置依賴的對(duì)象
er := c.Provide(NewConfig)
if err != nil {
panic(err)
}
//設(shè)置依賴的對(duì)象
err = c.Provide(NewLogger)
if err != nil {
panic(err)
}
//執(zhí)行Handle()方法
//Handle依賴 Config 和 Logger,使用Invoke執(zhí)行方法時(shí)會(huì)自動(dòng)注入依賴(依賴的對(duì)象要傳入Provide方法中)
err = c.Invoke(Handle)
if err != nil {
panic(err)
}
// Output:
// [foo] You've been invoked
}dig提供了一個(gè)容器(container),所有的依賴項(xiàng)通過Provide方法添加,執(zhí)行某個(gè)方法時(shí)使用Invoke方法,該方法會(huì)自動(dòng)注入所需要的依賴。
dig使用反射機(jī)制解決DI問題,所以代碼執(zhí)行性能上會(huì)有損耗
并且因?yàn)槭褂梅瓷渌钥赡艹霈F(xiàn)編譯時(shí)沒有錯(cuò)誤,執(zhí)行時(shí)報(bào)空指針
詳情使用方法可以參考官方文檔,dig可以繼承到gin框架中,有興趣的可以看看資料。
筆者不太喜歡這種使用方式,為了依賴注入破壞了代碼原有的調(diào)用方式。
基于代碼生成的DI
wire庫(kù)是google出的解決golang DI問題的工具,它可以 自動(dòng)生成依賴注入的代碼,節(jié)省了手動(dòng)去處理依賴關(guān)系
wire對(duì)原有代碼的侵入度很低,開發(fā)過程中,在依賴注入代碼處調(diào)用Build方法(例子中是初始化controller對(duì)象)就可以了
// +build wireinject
package main
import (
"encoding/json"
"fmt"
"github.com/google/wire"
"net/http"
)
type DataSource struct {
Operation string
}
func NewDataSource() DataSource {
return DataSource{Operation: "operation_name"}
}
//==================
type Dao struct {
DataSource DataSource
}
func NewDao(ds DataSource) *Dao {
return &Dao{
DataSource: ds,
}
}
func (d *Dao) GetItemList() ([]string, error) {
//TODO 拿到DB對(duì)象做查詢操作
fmt.Printf("db object: %s", d.DataSource.Operation)
return []string{d.DataSource.Operation, "item1", "item2"}, nil
}
//====================
type Service struct {
Dao *Dao
}
func NewService(dao *Dao) *Service {
return &Service{Dao: dao}
}
func (s *Service) GetItemList() ([]string, error) {
return s.Dao.GetItemList()
}
//=====================
type Controller struct {
Service *Service
}
func NewController(service *Service) *Controller {
return &Controller{Service: service}
}
func (c *Controller) GetItemList() ([]string, error) {
return c.Service.GetItemList()
}
var MegaSet = wire.NewSet(NewDataSource, NewDao, NewService, NewController)
func initializeController() *Controller {
wire.Build(MegaSet)
return &Controller{}
}
func getItemList(w http.ResponseWriter, r *http.Request) {
controller := initializeController()
itemList, _ := controller.GetItemList()
output, _ := json.Marshal(itemList)
fmt.Fprintf(w, string(output))
}
func main() {
http.HandleFunc("/items", getItemList)
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic(err)
}
}然后再項(xiàng)目根目錄執(zhí)行wire命令,會(huì)生成構(gòu)建好依賴關(guān)系的代碼(以_gen結(jié)尾的文件)
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//+build !wireinject
package main
import (
"encoding/json"
"fmt"
"github.com/google/wire"
"net/http"
)
// Injectors from main.go:
// 此處是生成的代碼
func initializeController() *Controller {
dataSource := NewDataSource()
dao := NewDao(dataSource)
service := NewService(dao)
controller := NewController(service)
return controller
}
// main.go:
type DataSource struct {
Operation string
}
func NewDataSource() DataSource {
return DataSource{Operation: "operation_name"}
}
type Dao struct {
DataSource DataSource
}
func NewDao(ds DataSource) *Dao {
return &Dao{
DataSource: ds,
}
}
func (d *Dao) GetItemList() ([]string, error) {
fmt.Printf("db object: %s", d.DataSource.Operation)
return []string{d.DataSource.Operation, "item1", "item2"}, nil
}
type Service struct {
Dao *Dao
}
func NewService(dao *Dao) *Service {
return &Service{Dao: dao}
}
func (s *Service) GetItemList() ([]string, error) {
return s.Dao.GetItemList()
}
type Controller struct {
Service *Service
}
func NewController(service *Service) *Controller {
return &Controller{Service: service}
}
func (c *Controller) GetItemList() ([]string, error) {
return c.Service.GetItemList()
}
var MegaSet = wire.NewSet(NewDataSource, NewDao, NewService, NewController)
func getItemList(w http.ResponseWriter, r *http.Request) {
controller := initializeController()
itemList, _ := controller.GetItemList()
output, _ := json.Marshal(itemList)
fmt.Fprintf(w, string(output))
}
func main() {
http.HandleFunc("/items", getItemList)
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic(err)
}
}關(guān)鍵代碼:
//執(zhí)行wire命令前的代碼
func initializeController() *Controller {
wire.Build(MegaSet)
return &Controller{}
}
//執(zhí)行后生成的代碼
// Injectors from main.go:
func initializeController() *Controller {
dataSource := NewDataSource()
dao := NewDao(dataSource)
service := NewService(dao)
controller := NewController(service)
return controller
}通過生成代碼解決依賴注入的問題,既能提升開發(fā)效率,又不影響代碼性能,wire更高級(jí)的用法可以去github document查看
- tips: 如果報(bào)錯(cuò)誤
other declaration of xxxx,請(qǐng)?jiān)谠次募^加上//+build wireinject - go-zero框架也是用wire解決DI問題
到此這篇關(guān)于淺析golang的依賴注入的文章就介紹到這了,更多相關(guān)go依賴注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Go+GoQuery庫(kù)實(shí)現(xiàn)頭條新聞采集
在本文中,我們將介紹如何使用Go語(yǔ)言和GoQuery庫(kù)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的爬蟲程序,用于抓取頭條新聞的網(wǎng)頁(yè)內(nèi)容,我們還將使用爬蟲代理服務(wù),提高爬蟲程序的性能和安全性,我們將使用多線程技術(shù),提高采集效率,最后,我們將展示爬蟲程序的運(yùn)行結(jié)果和代碼,需要的朋友可以參考下2023-10-10
go?sync.Once實(shí)現(xiàn)高效單例模式詳解
這篇文章主要為大家介紹了go?sync.Once實(shí)現(xiàn)高效單例模式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Go語(yǔ)言同步與異步執(zhí)行多個(gè)任務(wù)封裝詳解(Runner和RunnerAsync)
這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言同步與異步執(zhí)行多個(gè)任務(wù)封裝(Runner和RunnerAsync)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01
Go語(yǔ)言reflect包的反射機(jī)制基本用法示例
反射在處理接口和類型斷言、開發(fā)通用功能或者設(shè)計(jì)框架時(shí)尤為重要,本文將深入探索 Go 語(yǔ)言中的反射機(jī)制,通過具體的示例展示如何使用?reflect?包,讓你能夠在 Go 項(xiàng)目中有效地利用這一強(qiáng)大的工具2023-11-11

