欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang?依賴注入經(jīng)典解決方案uber/fx理論解析

 更新時(shí)間:2023年05月12日 11:31:57   作者:ag9920  
這篇文章主要為大家介紹了Golang依賴注入經(jīng)典解決方案uber/fx理論解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

開篇

今天繼續(xù)我們的【依賴注入開源解決方案系列】, Dependency Injection 業(yè)界的開源庫(kù)非常多,大家可以憑借自己的喜好也業(yè)務(wù)的復(fù)雜度來(lái)選型。基于 github star 數(shù)量以及方案的全面性,易用性上。推薦這兩個(gè):

1.【代碼生成】派系推薦大家用 wire, 做的事情非常輕量級(jí),省下大家手寫代碼的負(fù)擔(dān),沒有太多 DI 工具帶來(lái)的結(jié)構(gòu)性改造;

2.【反射】派系推薦大家用 uber/fx,功能非常強(qiáng)大,很全面,也比較符合直覺。

二者都需要顯式聲明依賴,這一點(diǎn)對(duì)程序的可讀性是好事,兩個(gè)庫(kù)的 star 也都非常多。建議大家有興趣的話研讀一下。不管是 codegen 還是 reflect(結(jié)合 interface{},泛型)都是 Golang 學(xué)習(xí)體系中必須的能力,否則很難實(shí)現(xiàn)通用的一些能力。

今天我們來(lái)看看 uber/fx 這個(gè)反射派系的經(jīng)典之作,這是 uber 家基于 dig 的又一步進(jìn)化。

uber/fx

Fx is a dependency injection system for Go.

fx 是 uber 2017 年開源的依賴注入解決方案,不僅僅支持常規(guī)的依賴注入,還支持生命周期管理。
從官方的視角看,fx 能為開發(fā)者提供的三大優(yōu)勢(shì):

代碼復(fù)用:方便開發(fā)者構(gòu)建松耦合,可復(fù)用的組件;

消除全局狀態(tài):Fx 會(huì)幫我們維護(hù)好單例,無(wú)需借用 init() 函數(shù)或者全局變量來(lái)做這件事了;

經(jīng)過(guò)多年 Uber 內(nèi)部驗(yàn)證,足夠可信。

我們從 uber-go/fx 看到的是 v1 的版本,fx 是遵循 SemVer 規(guī)范的,保障了兼容性,這一點(diǎn)大家可以放心。

從劣勢(shì)的角度分析,其實(shí) uber/fx 最大的劣勢(shì)還是大量使用反射,導(dǎo)致項(xiàng)目啟動(dòng)階段會(huì)需要一些性能消耗,但這一般是可以接受的。如果對(duì)性能有高要求,建議還是采取 wire 這類 codegen 的依賴注入解法。

目前市面上對(duì) Fx 的介紹文章并不多,筆者在學(xué)習(xí)的時(shí)候也啃了很長(zhǎng)時(shí)間官方文檔,這一點(diǎn)有好有壞。的確,再多的例子,再多的介紹,也不如一份完善的官方文檔更有力。但同時(shí)也給初學(xué)者帶來(lái)較高的門檻。

今天這篇文章希望從一個(gè)開發(fā)者的角度,帶大家理解 Fx 如何使用。

添加 fx 的依賴需要用下面的命令:

go get go.uber.org/fx@v1

后面我們會(huì)有專門的一篇文章,拿一個(gè)實(shí)戰(zhàn)項(xiàng)目來(lái)給大家展示,如何使用 Fx,大家同時(shí)也可以參考官方 README 中的 Getting Started 來(lái)熟悉。

下面一步一步來(lái),我們先來(lái)看看 uber/fx 中的核心概念。

provider 聲明依賴關(guān)系

在我們的業(yè)務(wù)服務(wù)的聲明周期中,對(duì)于各個(gè) module 的初始化應(yīng)該基于我們的 dependency graph 來(lái)合理進(jìn)行。先初始化無(wú)外部依賴的對(duì)象,隨后基于這些對(duì)象,初始化對(duì)它們有依賴的對(duì)象。

Provider 就是我們常說(shuō)的構(gòu)造器,能夠提供對(duì)象的生成邏輯。在 Fx 啟動(dòng)時(shí)會(huì)創(chuàng)建一個(gè)容器,我們需要將業(yè)務(wù)的構(gòu)造器傳進(jìn)來(lái),作為 Provider。類似下面這樣:

app = fx.New(
   fx.Provide(newZapLogger),
   fx.Provide(newRedisClient),
   fx.Provide(newMeaningOfLifeCacheRedis),
   fx.Provide(newMeaningOfLifeHandler),
)

這里面的 newXXX 函數(shù),就是我們的構(gòu)造器,類似這樣:

func NewLogger() *log.Logger {
    logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)
    logger.Print("Executing NewLogger.")
    return logger
}

我們只需要通過(guò) fx.Provide 方法傳入進(jìn)容器,就完成了將對(duì)象提供出去的使命。隨后 fx 會(huì)在需要的時(shí)候調(diào)用我們的 Provider,生成單例對(duì)象使用。

當(dāng)然,構(gòu)造器不光是這種沒有入?yún)⒌?。還有一些對(duì)象是需要顯式的傳入依賴:

func NewHandler(logger *log.Logger) (http.Handler, error) {
    logger.Print("Executing NewHandler.")
    return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
        logger.Print("Got a request.")
    }), nil
}

注意,這里返回的 http.Handler 也可以成為別人的依賴。這些,我們通通不用關(guān)心!

fx 會(huì)自己通過(guò)反射,搞明白哪個(gè) Provider 需要什么,能提供什么。構(gòu)建出來(lái)整個(gè) dependency graph。

// Provide registers any number of constructor functions, teaching the
// application how to instantiate various types. The supplied constructor
// function(s) may depend on other types available in the application, must
// return one or more objects, and may return an error. For example:
//
//    // Constructs type *C, depends on *A and *B.
//    func(*A, *B) *C
//
//    // Constructs type *C, depends on *A and *B, and indicates failure by
//    // returning an error.
//    func(*A, *B) (*C, error)
//
//    // Constructs types *B and *C, depends on *A, and can fail.
//    func(*A) (*B, *C, error)
//
// The order in which constructors are provided doesn't matter, and passing
// multiple Provide options appends to the application's collection of
// constructors. Constructors are called only if one or more of their returned
// types are needed, and their results are cached for reuse (so instances of a
// type are effectively singletons within an application). Taken together,
// these properties make it perfectly reasonable to Provide a large number of
// constructors even if only a fraction of them are used.
//
// See the documentation of the In and Out types for advanced features,
// including optional parameters and named instances.
//
// Constructor functions should perform as little external interaction as
// possible, and should avoid spawning goroutines. Things like server listen
// loops, background timer loops, and background processing goroutines should
// instead be managed using Lifecycle callbacks.
func Provide(constructors ...interface{}) Option {
    return provideOption{
        Targets: constructors,
        Stack:   fxreflect.CallerStack(1, 0),
    }
}

作為開發(fā)者,我們只需要保證,所有我們需要的依賴,都通過(guò) fx.Provide 函數(shù)提供即可。另外需要注意,雖然上面我們是每個(gè) fx.Provide,都只包含一個(gè)構(gòu)造器,實(shí)際上他是支持多個(gè)構(gòu)造器的。
module 模塊化組織依賴

// Module is a named group of zero or more fx.Options.
// A Module creates a scope in which certain operations are taken
// place. For more information, see [Decorate], [Replace], or [Invoke].
func Module(name string, opts ...Option) Option {
    mo := moduleOption{
        name:    name,
        options: opts,
    }
    return mo
}

fx 中的 module 也是經(jīng)典的概念。實(shí)際上我們?cè)谶M(jìn)行軟件開發(fā)時(shí),分層分包是不可避免的。而 fx 也是基于模塊化編程。使用 module 能夠幫助我們更方便的管理依賴:

/ ProvideLogger to fx  
func ProvideLogger() *zap.SugaredLogger {  
logger, _ := zap.NewProduction()  
slogger := logger.Sugar()  
return slogger  
}  
// Module provided to fx  
var Module = fx.Options(  
fx.Provide(ProvideLogger),  
)

我們的 Module 是一個(gè)可導(dǎo)出的變量,包含了一組 fx.Option,這里包含了各個(gè) Provider。
這樣,我們就不必要在容器初始化時(shí)傳入那么多 Provider 了,而是每個(gè) Module 干好自己的事即可。

func main() {  
    fx.New(  
        fx.Provide(http.NewServeMux),  
        fx.Invoke(server.New),  
        fx.Invoke(registerHooks),  
        loggerfx.Module,  
    ).Run()  
}

lifecycle 給應(yīng)用生命周期加上鉤子

// Lifecycle allows constructors to register callbacks that are executed on
// application start and stop. See the documentation for App for details on Fx
// applications' initialization, startup, and shutdown logic.
type Lifecycle interface {
    Append(Hook)
}
// A Hook is a pair of start and stop callbacks, either of which can be nil.
// If a Hook's OnStart callback isn't executed (because a previous OnStart
// failure short-circuited application startup), its OnStop callback won't be
// executed.
type Hook struct {
    OnStart func(context.Context) error
    OnStop  func(context.Context) error
}

lifecycle 是 Fx 定義的一個(gè)接口。我們可以對(duì) fx.Lifecycle 進(jìn)行 append 操作,增加鉤子函數(shù),這里就可以支持我們訂閱一些指定行為,如 OnStart 和 OnStop。

如果執(zhí)行某個(gè) OnStart 鉤子時(shí)出現(xiàn)錯(cuò)誤,應(yīng)用會(huì)立刻停止后續(xù)的 OnStart,并針對(duì)此前已經(jīng)執(zhí)行過(guò) OnStart 的鉤子執(zhí)行對(duì)應(yīng)的 OnStop 用于清理資源。

這里 fx 加上了 15 秒的超時(shí)限制,通過(guò) context.Context 實(shí)現(xiàn),大家記得控制好自己的鉤子函數(shù)執(zhí)行時(shí)間。

invoker 應(yīng)用的啟動(dòng)器

provider 是懶加載的,僅僅 Provide 出來(lái)我們的構(gòu)造器,是不會(huì)當(dāng)時(shí)就觸發(fā)調(diào)用的,而 invoker 則能夠直接觸發(fā)業(yè)務(wù)提供的函數(shù)運(yùn)行。并且支持傳入一個(gè) fx.Lifecycle 作為入?yún)?,業(yè)務(wù)可以在這里 append 自己想要的 hook。
假設(shè)我們有一個(gè) http server,希望在 fx 應(yīng)用啟動(dòng)的時(shí)候同步開啟。這個(gè)時(shí)候就需要兩個(gè)入?yún)ⅲ?/p>

fx.Lifecycle

我們的主依賴(通常是對(duì)服務(wù)接口的實(shí)現(xiàn),一個(gè) handler)

我們將這里的邏輯封裝起來(lái),就可以作為一個(gè) invoker 讓 Fx 來(lái)調(diào)用了。看下示例代碼:

func runHttpServer(lifecycle fx.Lifecycle, molHandler *MeaningOfLifeHandler) {
   lifecycle.Append(fx.Hook{OnStart: func(context.Context) error {
      r := fasthttprouter.New()
      r.Handle(http.MethodGet, "/what-is-the-meaning-of-life", molHandler.Handle)
      return fasthttp.ListenAndServe("localhost:8080", r.Handler)
   }})
}

下面我們將它加入 Fx 容器初始化的流程中:

fx.New(
      fx.Provide(newZapLogger),
      fx.Provide(newRedisClient),
      fx.Provide(newMeaningOfLifeCacheRedis),
      fx.Provide(newMeaningOfLifeHandler),
      fx.Invoke(runHttpServer),
)

這樣在創(chuàng)建容器時(shí),我們的 runHttpServer 就會(huì)被調(diào)用,進(jìn)而注冊(cè)了服務(wù)啟動(dòng)的邏輯。這里我們需要一個(gè) MeaningOfLifeHandler,F(xiàn)x 會(huì)觀察到這一點(diǎn),進(jìn)而到 Provider 里面挨個(gè)找依賴,每個(gè)類型對(duì)應(yīng)一個(gè)單例對(duì)象,通過(guò)懶加載的方式獲取到 MeaningOfLifeHandler 的所有依賴,以及子依賴。

其實(shí) Invoker 更多意義上看,像是一個(gè)觸發(fā)器。

我們可以有很多 Provider,但什么時(shí)候去調(diào)用這些函數(shù),生成依賴呢?Invoker 就是做這件事的。

// New creates and initializes an App, immediately executing any functions
// registered via Invoke options. See the documentation of the App struct for
// details on the application's initialization, startup, and shutdown logic.
func New(opts ...Option) *App

最后,有了一個(gè)通過(guò) fx.New 生成的 fx 應(yīng)用,我們就可以通過(guò) Start 方法來(lái)啟動(dòng)了:

func main() {
? ?ctx, cancel := context.WithCancel(context.Background())
? ?kill := make(chan os.Signal, 1)
? ?signal.Notify(kill)
? ?go func() {
? ? ? <-kill
? ? ? cancel()
? ?}()
? ?app := fx.New(
? ? ? fx.Provide(newZapLogger),
? ? ? fx.Provide(newRedisClient),
? ? ? fx.Provide(newMeaningOfLifeCacheRedis),
? ? ? fx.Provide(newMeaningOfLifeHandler),
? ? ? fx.Invoke(runHttpServer),
? ?)
? ?if err := app.Start(ctx);err != nil{
? ? ? fmt.Println(err)
? ?}
}

當(dāng)然,有了一個(gè) fx 應(yīng)用后,我們可以直接 fx.New().Run() 來(lái)啟動(dòng),也可以隨后通過(guò) app.Start(ctx) 方法啟動(dòng),配合 ctx 的取消和超時(shí)能力。二者皆可。

fx.In 封裝多個(gè)入?yún)?/h2>

當(dāng)構(gòu)造函數(shù)參數(shù)過(guò)多的時(shí)候,我們可以使用 fx.In 來(lái)統(tǒng)一注入,而不用在構(gòu)造器里一個(gè)個(gè)加參數(shù):

type ConstructorParam struct {
? ? fx.In
? ? Logger ?*log.Logger
? ? Handler http.Handler
}
type Object struct {
? ? Logger ?*log.Logger
? ? Handler http.Handler
}
func NewObject(p ConstructorParam) Object {
? ? return Object {
? ? ? ? Logger: ?p.Logger,
? ? ? ? Handler: p.Handler,
? ? }
}

fx.Out 封裝多個(gè)出參

和 In 類似,有時(shí)候我們需要返回多個(gè)參數(shù),這時(shí)候一個(gè)個(gè)寫顯然比較笨重。我們可以用 fx.Out 的能力用結(jié)構(gòu)體來(lái)封裝:

type Result struct {
? ? fx.Out
? ? Logger ?*log.Logger
? ? Handler http.Handler
}
func NewResult() Result {
? ? // logger := xxx
? ? // handler := xxx
? ? return Result {
? ? ? ? Logger: ?logger,
? ? ? ? Handler: handler,
? ? }
}

基于同類型提供多種實(shí)現(xiàn)

By default, Fx applications only allow one constructor for each type.

Fx 應(yīng)用默認(rèn)只允許每種類型存在一個(gè)構(gòu)造器,這種限制在一些時(shí)候是很痛的。

有些時(shí)候我們就是會(huì)針對(duì)一個(gè) interface 提供多種實(shí)現(xiàn),如果做不到,我們就只能在外面套一個(gè)類型,這和前一篇文章中我們提到的 wire 里的處理方式是一樣的:

type RedisA *redis.Client
type RedisB *redis.Client

但這樣還是很笨重,有沒有比較優(yōu)雅的解決方案呢?
當(dāng)然有,要不 uber/fx 怎么能被稱為一個(gè)功能全面的 DI 方案呢?
既然是同類型,多個(gè)不同的值,我們可以給不同的實(shí)現(xiàn)命名來(lái)區(qū)分。進(jìn)而這涉及兩個(gè)部分:生產(chǎn)端 和 消費(fèi)端。

在提供依賴的時(shí)候,可以聲明它的名稱,進(jìn)而即便出現(xiàn)同類型的其他依賴,fx 也知道如何區(qū)分。
在獲取依賴的時(shí)候,也要指明我們需要的依賴的名稱具體是什么,而不只是簡(jiǎn)單的明確類型即可。

這里我們需要用到 fx.In 和 fx.Out 的能力。參照 官方文檔 我們來(lái)了解一下 fx 的解法:Named Values。
fx 支持開發(fā)者聲明 name 標(biāo)簽,用來(lái)給依賴「起名」,類似這樣:name:"rw"。

type GatewayParams struct {
? fx.In
? WriteToConn ?*sql.DB `name:"rw"`
? ReadFromConn *sql.DB `name:"ro" optional:"true"`
}
func NewCommentGateway(p GatewayParams, log *log.Logger) (*CommentGateway, error) {
? if p.ReadFromConn == nil {
? ? log.Print("Warning: Using RW connection for reads")
? ? p.ReadFromConn = p.WriteToConn
? }
? // ...
}
type ConnectionResult struct {
? fx.Out
? ReadWrite *sql.DB `name:"rw"`
? ReadOnly ?*sql.DB `name:"ro"`
}
func ConnectToDatabase(...) (ConnectionResult, error) {
? // ...
? return ConnectionResult{ReadWrite: rw, ReadOnly: ?ro}, nil
}

這樣 fx 就知道,我們?nèi)?gòu)建 NewCommentGateway 的時(shí)候,傳入的 *sql.DB 需要是 rw 這個(gè)名稱的。而此前ConnectToDatabase 已經(jīng)提供了這個(gè)名稱,同類型的實(shí)例,所以依賴構(gòu)建成功。

使用起來(lái)非常簡(jiǎn)單,在我們對(duì) In 和 Out 的 wrapper 中聲明各個(gè)依賴的 name,也可以搭配 optional 標(biāo)簽使用。fx 支持任意多個(gè) name 的實(shí)例。

這里需要注意,同名稱的生產(chǎn)端和消費(fèi)端的類型必須一致,不能一個(gè)是 sql.DB 另一個(gè)是 *sql.DB。命名的能力只有在同類型的情況下才有用處。

Annotate 注解器

Annotate lets you annotate a function's parameters and returns without you having to declare separate struct definitions for them.

注解器能幫我們修改函數(shù)的入?yún)⒑统鰠?,無(wú)需定義單獨(dú)的結(jié)構(gòu)體。fx 的這個(gè)能力非常強(qiáng)大,目前暫時(shí)沒有看到其他 DI 工具能做到這一點(diǎn)。

func Annotate(t interface{}, anns ...Annotation) interface{} {
    result := annotated{Target: t}
    for _, ann := range anns {
        if err := ann.apply(&result); err != nil {
            return annotationError{
                target: t,
                err:    err,
            }
        }
    }
    return result
}

我們來(lái)看看如何用 Annotate 來(lái)添加 ParamTag, ResultTag 來(lái)實(shí)現(xiàn)同一個(gè) interface 多種實(shí)現(xiàn)。

// Given,
type Doer interface{ ... }
// And three implementations,
type GoodDoer struct{ ... }
func NewGoodDoer() *GoodDoer
type BadDoer struct{ ... }
func NewBadDoer() *BadDoer
type UglyDoer struct{ ... }
func NewUglyDoer() *UglyDoer
fx.Provide(
? fx.Annotate(NewGoodDoer, fx.As(new(Doer)), fx.ResultTags(`name:"good"`)),
? fx.Annotate(NewBadDoer, fx.As(new(Doer)), fx.ResultTags(`name:"bad"`)),
? fx.Annotate(NewUglyDoer, fx.As(new(Doer)), fx.ResultTags(`name:"ugly"`)),
)

這里我們有 Doer 接口,以及對(duì)應(yīng)的三種實(shí)現(xiàn):GoodDoer, BadDoer, UglyDoer,三種實(shí)現(xiàn)的構(gòu)造器返回值甚至都不需要是Doer,完全可以是自己的 struct 類型。

這里還是不得不感慨 fx 強(qiáng)大的裝飾器能力。我們用一個(gè)簡(jiǎn)單的:

fx.Annotate(NewGoodDoer, fx.As(new(Doer))) 

就可以對(duì)構(gòu)造器 NewGoodDoer 完成類型轉(zhuǎn)換。
這里還可以寫一個(gè) helper 函數(shù)簡(jiǎn)化一下處理:

func AsDoer(f any, name string) any {
? return fx.Anntoate(f, fx.As(new(Doer)), fx.ResultTags("name:" + strconv.Quote(name)))
}
fx.Provide(
?AsDoer(NewGoodDoer, "good"),
?AsDoer(NewBadDoer, "bad"),
?AsDoer(NewUglyDoer, "ugly"),
)

與之相對(duì)的,提供依賴的時(shí)候我們用 ResultTag,消費(fèi)依賴的時(shí)候需要用 ParamTag。

func Do(good, bad, ugly Doer) {
? // ...
}
fx.Invoke(
? fx.Annotate(Do, fx.ParamTags(`name:"good"`, `name:"bad"`, `name:"ugly"`)),
)
這樣就無(wú)需通過(guò) fx.In 和 fx.Out 的封裝能力來(lái)實(shí)現(xiàn)了,非常簡(jiǎn)潔。
當(dāng)然,如果我們上面的返回值直接就是 interface,那么久不需要 fx.As 這一步轉(zhuǎn)換了。
go復(fù)制代碼func NewGateway(ro, rw *db.Conn) *Gateway { ... }
fx.Provide(
? fx.Annotate(
? ? NewGateway,
? ? fx.ParamTags(`name:"ro" optional:"true"`, `name:"rw"`),
? ? fx.ResultTags(`name:"foo"`),
? ),
)

和下面的實(shí)現(xiàn)是等價(jià)的:

type params struct {
? fx.In
? RO *db.Conn `name:"ro" optional:"true"`
? RW *db.Conn `name:"rw"`
}
type result struct {
? fx.Out
? GW *Gateway `name:"foo"`
}
fx.Provide(func(p params) result {
? ?return result{GW: NewGateway(p.RO, p.RW)}
})

這里需要注意存在兩個(gè)限制:

Annotate 不能應(yīng)用于包含 fx.In 和 fx.Out 的函數(shù),它的存在本身就是為了簡(jiǎn)化;
不能在一個(gè) Annotate 中多次使用同一個(gè)注解,比如下面這個(gè)例子會(huì)報(bào)錯(cuò):

fx.Provide(
  fx.Annotate(
    NewGateWay,
    fx.ParamTags(`name:"ro" optional:"true"`),
    fx.ParamTags(`name:"rw"), // ERROR: ParamTags was already used above
    fx.ResultTags(`name:"foo"`)
  )
)

小結(jié)

這里是 uber/fx 的理論篇,我們了解了 fx 的核心概念和基礎(chǔ)用法。和 wire 一樣,它們都要求強(qiáng)制編寫構(gòu)造函數(shù),有額外的編碼成本。但好處在于功能全面、設(shè)計(jì)比較優(yōu)雅,對(duì)業(yè)務(wù)代碼無(wú)侵入。

下一篇,我們會(huì)從實(shí)戰(zhàn)的角度,基于 cloudwego 社區(qū)的 Kitex 框架,看看怎么基于 uber/fx 實(shí)現(xiàn)優(yōu)雅的注入,敬請(qǐng)期待。

以上就是 Golang 依賴注入經(jīng)典解決方案 uber/fx 理論篇的詳細(xì)內(nèi)容,更多關(guān)于 Golang 依賴注入經(jīng)典解決方案 uber/fx 理論篇的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang常用包使用介紹

    Golang常用包使用介紹

    標(biāo)準(zhǔn)的Go語(yǔ)言代碼庫(kù)中包含了大量的包,并且在安裝Go的時(shí)候多數(shù)會(huì)自動(dòng)安裝到系統(tǒng)中。我們可以在$GOROOT/src/pkg目錄中查看這些包。下面簡(jiǎn)單介紹一些我們開發(fā)中常用的包
    2022-09-09
  • Go語(yǔ)言中三個(gè)輸入函數(shù)(scanf,scan,scanln)的區(qū)別解析

    Go語(yǔ)言中三個(gè)輸入函數(shù)(scanf,scan,scanln)的區(qū)別解析

    本文詳細(xì)介紹了Go語(yǔ)言中三個(gè)輸入函數(shù)Scanf、Scan和Scanln的區(qū)別,包括用法、功能和輸入終止條件等,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-10-10
  • Go語(yǔ)言集成開發(fā)環(huán)境之VS Code安裝使用

    Go語(yǔ)言集成開發(fā)環(huán)境之VS Code安裝使用

    VS Code是微軟開源的一款編輯器,插件系統(tǒng)十分的豐富,下面介紹如何用VS Code搭建go語(yǔ)言開發(fā)環(huán)境,需要的朋友可以參考下
    2021-10-10
  • 簡(jiǎn)單談?wù)凣olang中的字符串與字節(jié)數(shù)組

    簡(jiǎn)單談?wù)凣olang中的字符串與字節(jié)數(shù)組

    這篇文章主要給大家介紹了關(guān)于Golang中字符串與字節(jié)數(shù)組的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Golang具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • golang實(shí)現(xiàn)aes-cbc-256加密解密功能

    golang實(shí)現(xiàn)aes-cbc-256加密解密功能

    這篇文章主要介紹了golang實(shí)現(xiàn)aes-cbc-256加密解密功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • 在?Go?語(yǔ)言中使用?regexp?包處理正則表達(dá)式的操作

    在?Go?語(yǔ)言中使用?regexp?包處理正則表達(dá)式的操作

    正則表達(dá)式是處理字符串時(shí)一個(gè)非常強(qiáng)大的工具,而?Go?語(yǔ)言的?regexp?包提供了簡(jiǎn)單而強(qiáng)大的接口來(lái)使用正則表達(dá)式,本文將介紹如何在?Go?中使用?regexp?包來(lái)編譯和執(zhí)行正則表達(dá)式,以及如何從文本中匹配和提取信息,感興趣的朋友一起看看吧
    2023-12-12
  • Go和Java算法詳析之分?jǐn)?shù)到小數(shù)

    Go和Java算法詳析之分?jǐn)?shù)到小數(shù)

    這篇文章主要給大家介紹了關(guān)于Go和Java算法詳析之分?jǐn)?shù)到小數(shù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-08-08
  • 揭秘Go Json.Unmarshal精度丟失之謎

    揭秘Go Json.Unmarshal精度丟失之謎

    我們知道在json反序列化時(shí)是沒有整型和浮點(diǎn)型的區(qū)別,數(shù)字都使用同一種類型,在go語(yǔ)言的類型中這種共同類型就是float64,下面我們就來(lái)探討一下Json.Unmarshal精度丟失之謎吧
    2023-08-08
  • Go語(yǔ)言實(shí)現(xiàn)JSON解析的神器詳解

    Go語(yǔ)言實(shí)現(xiàn)JSON解析的神器詳解

    php轉(zhuǎn)go是大趨勢(shì),越來(lái)越多公司的php服務(wù)都在用go進(jìn)行重構(gòu),重構(gòu)過(guò)程中,會(huì)發(fā)現(xiàn)php的json解析操作是真的香。本文和大家分享了一個(gè)Go語(yǔ)言實(shí)現(xiàn)JSON解析的神器,希望對(duì)大家有所幫助
    2023-01-01
  • go引入自建包名報(bào)錯(cuò):package?XXX?is?not?in?std解決辦法

    go引入自建包名報(bào)錯(cuò):package?XXX?is?not?in?std解決辦法

    這篇文章主要給大家介紹了go引入自建包名報(bào)錯(cuò):package?XXX?is?not?in?std的解決辦法,這是在寫測(cè)試引入包名的時(shí)候遇到的錯(cuò)誤提示,文中將解決辦法介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12

最新評(píng)論