英文:
What is the best way to have dependency injection in Golang
问题
我是一名Golang的初学者,正在开发一个小型库,需要在代码的某个地方获取数据库连接,以供不同的子包/方法调用使用。我想知道如何管理这个问题?
例如,如果我成功创建了一个Web服务器,它可以通过处理程序(handler)工作,那么我该如何在这个函数内获取数据库连接呢?它可以与另一个进程一起使用,也可以通过简单的方法调用或MVC模型来实现吗?
我不想使用全局变量,因为对我来说这是一种不好的做法,除非在非常特殊的情况下(或者以某种巧妙的方式)。
我在不同的网站上阅读了很多文章,但仍然想听听不同的意见和经验。
谢谢你的时间!
英文:
I'm a beginner in Golang and I'm working on a small library which need to get a DB connection at some point in the code for différent sub package / method call. I'm just wondering how I can manage this ?
Example, If I manage to have a webserver, it works with handler, so how can I get this connection inside this function ?
It could be used with another process, simple method call or MVC model ?
I don't want to use global because for me it's a bad practice except if it's very exceptional way (or tricky somehow).
I read a lot of write in different website, but still, I'm asking and learning from different opinion and experiences.
Thanks for your time !
答案1
得分: 3
创建一个代表资源的结构体,我们称之为Cart。在这个结构体中添加get和post方法。这些方法应该是HTTP处理程序。在main函数中,使用db接口创建一个结构体实例。在路由中调用Cart.get方法。现在在get方法中,你可以访问db接口。
这只是一个示例,用于理解测试时的注入概念。
type storage interface {
PrepareContext(context.Context, string) (*sql.Stmt, error)
}
func main() {
db, _ := sql.Open("mysql", `queryString`)
http.HandleFunc("/", Cart{db}.get)
http.ListenAndServe(":8080", nil)
}
type Cart struct {
storage
}
func (crt Cart) get(w http.ResponseWriter, r *http.Request) {
q, _ := crt.PrepareContext(context.Background(), "select *")
fmt.Println(q.Exec())
}
/////////测试
type testDB struct{}
func (c testDB) PrepareContext(context.Context, string) (*sql.Stmt, error) {
return nil, nil
}
func TestGet(t *testing.T) {
db := testDB{}
_ = Cart{db}
// 进行HTTP测试
}
英文:
Create a struct that represent the resource, let's call Cart. Add get and post methods to this struct. These methods should be http handlers. In main create an instance of the struct with db interface. And in the route call Cart.get. Now in get method you have access to the db interface.
Not a working example, just to get the idea of injecting for testing.
type storage interface {
PrepareContext(context.Context, string) (*sql.Stmt, error)
}
func main() {
db, _ := sql.Open("mysql", `queryString`)
http.HandleFunc("/", Cart{db}.get)
http.ListenAndServe(":8080", nil)
}
type Cart struct {
storage
}
func (crt Cart) get(w http.ResponseWriter, r *http.Request) {
q, _ := crt.PrepareContext(context.Background(), `select *`)
fmt.Println(q.Exec())
}
/////////Test
type testDB struct{}
func (c testDB) PrepareContext(context.Context, string) (*sql.Stmt, error) {
return nil, nil
}
func TestGet(t *testing.T) {
db := testDB{}
_ = Cart{db}
//http test here
}
答案2
得分: 1
我建议使用Dargo,它是一个类似于Java CDI和/或JSR-330的注入引擎。它使用结构注解,并且可以通过反射或使用创建函数进行注入。它支持不同的服务生命周期,包括Singleton(仅创建一次,延迟创建)、PerLookup(每次注入或查找时创建)、Immediate(仅创建一次,立即创建)和DargoContext(与context.Context的生命周期相关联)。
英文:
I would suggest Dargo which is an injection engine in the style of Java CDI and/or JSR-330. It uses struct annotations and Injection is performed using reflection or using creator functions. It supports different lifecycles for services including Singleton (created exactly once, lazily), PerLookup (created every time injected or looked up), Immediate (created exactly once, eagerly) and DargoContext (tied to the lifecycle of a context.Context)
答案3
得分: 1
你也可以尝试使用Hiboot,它是一个支持依赖注入的Web/CLI应用程序框架。
// HelloService是一个简单的服务接口,通过接口,我们可以在单元测试中模拟一个假的服务
type HelloService interface {
SayHello(name string) string
}
type helloServiceImpl struct {
}
func init() {
// 通过构造函数newHelloController注册Rest Controller
// 通过构造函数newHelloService注册Service
app.Register(newHelloController, newHelloService)
}
// 请注意构造函数HelloService的返回类型名称,
// hiboot将实例化一个名为helloService的实例进行依赖注入
func newHelloService() HelloService {
return &helloServiceImpl{}
}
// SayHello是一个服务方法的实现
func (s *helloServiceImpl) SayHello(name string) string {
return "Hello" + name
}
// PATH: /login
type helloController struct {
web.Controller
helloService HelloService
}
// 通过参数helloService HelloService在构造函数中注入helloService
func newHelloController(helloService HelloService) *helloController {
return &helloController{
helloService: helloService,
}
}
// Get /
// 方法名的第一个单词是HTTP方法GET
func (c *helloController) Get(name string) string {
return c.helloService.SayHello(name)
}
英文:
You can also try Hiboot which is a web/cli application framework that support dependency injection out of box.
// HelloService is a simple service interface, with interface, we can mock a fake service in unit test
type HelloService interface {
SayHello(name string) string
}
type helloServiceImpl struct {
}
func init() {
// Register Rest Controller through constructor newHelloController
// Register Service through constructor newHelloService
app.Register(newHelloController, newHelloService)
}
// please note that the return type name of the constructor HelloService,
// hiboot will instantiate a instance named helloService for dependency injection
func newHelloService() HelloService {
return &helloServiceImpl{}
}
// SayHello is a service method implementation
func (s *helloServiceImpl) SayHello(name string) string {
return "Hello" + name
}
// PATH: /login
type helloController struct {
web.Controller
helloService HelloService
}
// newHelloController inject helloService through the argument helloService HelloService on constructor
func newHelloController(helloService HelloService) *helloController {
return &helloController{
helloService: helloService,
}
}
// Get /
// The first word of method name is the http method GET
func (c *helloController) Get(name string) string {
return c.helloService.SayHello(name)
}
答案4
得分: 0
我建议尝试使用https://github.com/facebookgo/inject。它允许定义对象图并使用结构注释指定依赖关系。注入是通过反射进行的。
英文:
I would suggest giving a try to https://github.com/facebookgo/inject. It allows to define an object graph and specify dependencies with struct annotations. Injection is performed using reflection.
答案5
得分: 0
对于一个IoC容器,你可以尝试使用这个包:
https://github.com/golobby/container
单例绑定的示例:
container.Singleton(func() Database {
return &MySQL{}
})
解析的示例:
var db Database
container.Make(&db)
如你所见,使用起来非常简单。
英文:
For an IoC container, you can try this package:
https://github.com/golobby/container
Example of singleton binding:
container.Singleton(func() Database {
return &MySQL{}
})
Example of resolving:
var db Database
container.Make(&db)
As you can see it's so easy to work with.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论