【Go】go依赖注入库samber/do使用
介绍
以简单和高效而闻名的Go语言在其1.18版本中引入了泛型,这可以显着减少大量代码生成的需要,使该语言更加强大和灵活。如果您有兴趣, Go 泛型教程 是很好的学习资源。
通过使用 Go 的泛型,samber/do库为依赖注入 (DI) 提供了一个很好的解决方案。依赖注入是一种重要的设计模式,它促进对象及其依赖关系之间的松散耦合,从而提高代码模块化性、可测试性和可维护性。泛型和依赖注入的结合进一步提升了 Go 在创建高效、可扩展软件方面的潜力。在本文中,您将学习如何使用 samber/do 提供依赖注入。
代码结构
. ├── cmd │ └── web │ └── main.go ├── domain │ └── user.go ├── go.mod ├── go.sum └── user ├── handler.go ├── repository.go └── service.go
我们使用与这篇博客相同的示例,但使用samber/do 库来实现 DI 而不是 Google Wire。正如我们所看到的,代码的结构变得更加简单。您可以在 https://github.com/Shujie-Tan/do-example 找到源代码。
服务关系
domain /user.go定义了业务逻辑结构和接口,如下所示。
type ( User struct { ID string `json:"id"` Username string `json:"username"` } UserEntity struct { ID string Username string Password string } UserRepository interface { FetchByUsername(ctx context.Context, username string) (*UserEntity, error) } UserService interface { FetchByUsername(ctx context.Context, username string) (*User, error) } UserHandler interface { FetchByUsername() http.HandlerFunc } )
在用户目录下可以看到这些接口的实现。其关系可以表示为
UserHandler -> UserService -> UserRepository -> sql.DB
这意味着UserHandler依赖于UserService,而 UserService 又依赖于UserRepository,最后UserRepository依赖于sql.DB进行数据库操作。这些依赖关系可通过使用接口来反转。
这是一个很简单的例子。现在我们构建对象及其依赖关系。
cmd/web/main.go
package main import ( "database/sql" "example/domain" "example/user" "fmt" "net/http" _ "github.com/lib/pq" "github.com/samber/do" ) func main() { injector := do.New() // 1 connStr := "user=root dbname=mydb" db, err := sql.Open("postgres", connStr) // 2 if err != nil { panic(err) } defer db.Close() do.ProvideNamed[*sql.DB](injector, "user", func(i *do.Injector) (*sql.DB, error) { return db, nil }) // 3 do.Provide(injector, user.NewRepository) do.Provide(injector, user.NewService) do.Provide(injector, user.NewHandler) // 4 userHandler := do.MustInvoke[domain.UserHandler](injector) // 5 http.Handle("/user", userHandler.FetchByUsername()) fmt.Printf("Try run server at :%d\n", 8080) if err := http.ListenAndServe(":8080", nil); err != nil { fmt.Printf("Error: %v", err) } }
我们逐步分析一下代码:
main 函数首先使用
injector := do.New()
创建一个新的 DI 容器。该容器将用于管理应用程序对象的依赖关系。使用
sql.Open
函数建立与 PostgreSQL 数据库的连接。使用
do.ProvideNamed
函数将数据库连接添加到 DI 容器。该函数采用三个参数:DI 容器、依赖项的名称以及返回依赖项和错误的提供程序函数。在本例中,依赖项是数据库连接,该函数仅返回连接并返回 nil 来表示错误。使用
do.Provide
函数将repository、service和handler添加到 DI 容器。该函数有两个参数:DI 容器和返回依赖项和错误的函数。在本例中,函数是user.NewRepository
、user.NewService
和user.NewHandler
,它们分别创建repository、service和handler的实例。请注意提供程序函数的返回类型应该是接口,而不是具体类型。Go语言模式『接受接口,返回结构』将在 v2版本支持。使用
do.MustInvoke
函数从 DI 容器检索userHandler并将其注册到 http 包。该函数采用两个参数:DI 容器和要检索的依赖项的类型。在本例中,它检索用户处理程序并将其FetchByUsername方法注册为 /user 路由的处理程序。
用户/repository.go
package user import ( "context" "database/sql" "example/domain" "github.com/samber/do" ) type repository struct { db *sql.DB } func (r *repository) FetchByUsername(ctx context.Context, username string) (*domain.UserEntity, error) { // use db here } // the return type of NewRepository should be interface, rather than the concrete type! func NewRepository(i *do.Injector) (domain.UserRepository, error) { db := do.MustInvokeNamed[*sql.DB](i, "user") return &repository{db: db}, nil }
user/service.go
package user import ( "context" "example/domain" "github.com/samber/do" ) type service struct { repo domain.UserRepository } func (s *service) FetchByUsername(ctx context.Context, username string) (*domain.User, error) { // use repository here } func NewService(i *do.Injector) (domain.UserService, error) { repo := do.MustInvoke[domain.UserRepository](i) return &service{repo: repo}, nil }
user/handler.go
package user import ( "example/domain" "net/http" "github.com/samber/do" ) type handler struct { svc domain.UserService } func (h *handler) FetchByUsername() http.HandlerFunc { // use service here } func NewHandler(i *do.Injector) (domain.UserHandler, error) { svc := do.MustInvoke[domain.UserService](i) return &handler{svc: svc}, nil }
结论
在本文中,我们学习了如何使用samber/do在 Go 中提供依赖注入。我们已经了解了如何创建 DI 容器、向容器添加依赖项以及从容器中检索依赖项。我们还了解了如何使用容器来管理应用程序的依赖项。通过使用samber/do,我们可以创建更加模块化、可测试和可维护的代码,并充分利用 Go 的新泛型功能。
如果您有任何问题或反馈,请随时在下面发表评论。感谢您的阅读!
猜你喜欢
- 【Go】Go语言介绍
- 文章目录语法简单并发模型内存分配垃圾回收静态链接标准库工具链Go 起源Go 是编译型语言功能特点Go 语言特性Go有什么优势Go语言创始人参考文档Go 起源Go语言(或 Golang)起源于 2007 年,并在 2009 年正式对外发布。Go 是非常年轻的一门语言,它的主要目标是兼具 Python 等动态语言的开发速度和 C/C++ 等编译型语言的性能与安全性。Go语言是编程语言设计的又一次尝试,是对类C语言的重大改进,它不但能让你访问底层操作系统,还提供了强大的网络编程和并发编程支持。Go语言
- 【Go】Go语言支持哪些常见函数
- Go语言是一门开源的编程语言,由Google开发,在云计算和大数据领域广泛应用。它支持丰富的标准库,提供了许多常见的函数来帮助开发者编写高效、可靠的代码。在本文中,我们将探讨Go语言支持的一些常见函数,并提供具体的代码示例。1. 字符串处理函数Go语言提供了丰富的字符串处理函数,帮助开发者对字符串进行各种操作,比如拼接、分割、替换、查找等。以下是一些常见的字符串处理函数及其代码示例:package main import (
- 【Go】windows系统Go语言环境安装
- 1. Go语言安装包下载打开官网https://golang.google.cn/dl/,找到windows版本,点击下载,然后点击安装包安装,可自定义安装路径。2. GOROOT环境变量配置打开系统环境变量,新加GOROOT,路径就是Go安装根路径3. GOPATH环境变量添加在添加GOPATH环境变量之前,我们需要先新建一个工作区文件夹,这里笔者以GoProject命名。然后在该文件夹下分别创建src, pkg, bin三个文件夹,这三个文件夹作用如下:src: 存放源代码(go项目存放的位
- 【Go】go依赖注入库samber/do使用
- 介绍以简单和高效而闻名的Go语言在其1.18版本中引入了泛型,这可以显着减少大量代码生成的需要,使该语言更加强大和灵活。如果您有兴趣, Go 泛型教程 是很好的学习资源。通过使用 Go 的泛型,samber/do库为依赖注入 (DI) 提供了一个很好的解决方案。依赖注入是一种重要的设计模式,它促进对象及其依赖关系之间的松散耦合,从而提高代码模块化性、可测试性和可维护性。泛型和依赖注入的结合进一步提升了 Go 在创建高效、可扩展软件方面的潜力。在本文中,您将学习如何使用 samber/do 提供依
- 【Go】Go语言执行linux命令行
- ackage main /* Golang语言执行linux命令行 */ import ( "fmt" "io/ioutil" "os/exec" "time" ) func run() { cmd := exec.Command("/bin/bash", "-c",&n
- 【Go】使用Goland创建并运行项目
- 文章目录**前言**创建新项目编辑运行/调试配置编写并运行代码总结强烈推荐专栏集锦写在最后前言在Go语言的开发过程中,选择一个合适的集成开发环境(IDE)是提高效率和编写可维护代码的关键一步。JetBrains的Goland作为一款专门为Go语言开发的IDE,提供了丰富的功能和强大的工具,使得开发人员能够更轻松地构建和维护Go项目。本文将引导您通过Goland的简单步骤,创建和运行您的第一个Go语言项目。无论您是初学者还是经验丰富的Go开发者,Goland都能够提供出色的开发体验,帮助您更加专注
- 【Go】Golang标准库介绍(三)
- 1. image库 (常见图形格式的访问及生成)在 Go 语言的标准库中,image 包提供了对图像的基本操作和处理功能。这个包定义了 Image 接口和一些基本的图像类型,同时也包含了一些实现了该接口的具体类型,如 image.RGBA 和 image.Gray。关键概念和类型:1. Image 接口: 定义了图像的基本操作,包括获取像素值、设置像素值等。 2. RGBA 类型: 表示一个带有红、绿、蓝和透明度通道的图像。 3.&nb
- 【Go】Go语言的特点和优势
- Go语言是一种开源的编程语言,它在2009年由Google开发并在2012年正式发布。Go语言的主要设计目标是提供一种简单有效的方法来构建简单、可靠、高效的软件。 Go语言的主要特点和优势包括: 静态类型:Go语言是一种静态类型的语言,这意味着所有变量在编译时就必须确定其数据类型。 自动垃圾回收:Go语言具有自动的垃圾回收机制,这意味着开发者不需要手动管理内存。 并行进程:Go语言支持并行进程,这使得开发者可以利用多核处理器的优势来编写并发程序。&nbs