Preface
If you are doing web development, you will definitely be familiar with dependency injection. Java programmers have long been accustomed to the dependency injection provided by spring. It is very convenient for business development. You can only focus on business logic, and all dependencies between objects are left to the framework.
golang is a strongly typed language, and it is machine code after compilation, so it is generally usedreflectionorCode generationSolve the problem of dependency injection
Reflection-based DI
The framework for solving DI problems based on reflection is Uber's most commonly useddigLibrary
Official example:
type Config struct { Prefix string } //Initialize the Config functionfunc NewConfig()(*Config, error) { // In a real program, the configuration will probably be read from a // file. var cfg Config err := ([]byte(`{"prefix": "[foo] "}`), &cfg) return &cfg, err } //Initialize the logger functionfunc NewLogger(cfg *Config) * { return (, , 0) } func Handle() (l *) { ("You've been invoked") } func main() { //Initialize the dig object c := () //Provide method is used to set dependent objects er := (NewConfig) if err != nil { panic(err) } //Set dependent objects err = (NewLogger) if err != nil { panic(err) } //Execute Handle() method //Handle depends on Config and Logger. When using Invoke to execute methods, the dependency will be automatically injected (the dependent objects must be passed into the Provide method) err = (Handle) if err != nil { panic(err) } // Output: // [foo] You've been invoked }
dig provides a container, all dependencies are added through the Provide method, and the Invoke method is used when executing a method, which will automatically inject the required dependencies.
Dig uses reflection mechanism to solve DI problems, so there will be loss in code execution performance.
And because of using reflection, there may be no compile time errors, and the execution time null pointer is
For details, please refer to the official documentation. DIG can be inherited into the gin framework. If you are interested, you can read the information.
The author doesn't like this method of use very much. For dependency injection, it destroys the original calling method of the code.
Code-generated DI
wireThe library is a tool released by Google to solve golang DI problems. It can automatically generate dependency injection code, saving manual processing of dependencies.
github address
Wire has very low intrusion into the original code. During the development process, you can just call the Build method (instantly initialize the controller object) at the dependency injection code.
// +build wireinject package main import ( "encoding/json" "fmt" "/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 Get the DB object for query operation ("db object: %s", ) return []string{, "item1", "item2"}, nil } //==================== type Service struct { Dao *Dao } func NewService(dao *Dao) *Service { return &Service{Dao: dao} } func (s *Service) GetItemList() ([]string, error) { return () } //===================== type Controller struct { Service *Service } func NewController(service *Service) *Controller { return &Controller{Service: service} } func (c *Controller) GetItemList() ([]string, error) { return () } var MegaSet = (NewDataSource, NewDao, NewService, NewController) func initializeController() *Controller { (MegaSet) return &Controller{} } func getItemList(w , r *) { controller := initializeController() itemList, _ := () output, _ := (itemList) (w, string(output)) } func main() { ("/items", getItemList) err := (":8080", nil) if err != nil { panic(err) } }
Then execute the wire command in the project root directory, which will generate the code to build the dependency (file ending in _gen)
// Code generated by Wire. DO NOT EDIT. //go:generate go run /google/wire/cmd/wire //+build !wireinject package main import ( "encoding/json" "fmt" "/google/wire" "net/http" ) // Injectors from : // Here is the generated codefunc initializeController() *Controller { dataSource := NewDataSource() dao := NewDao(dataSource) service := NewService(dao) controller := NewController(service) return controller } // : 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) { ("db object: %s", ) return []string{, "item1", "item2"}, nil } type Service struct { Dao *Dao } func NewService(dao *Dao) *Service { return &Service{Dao: dao} } func (s *Service) GetItemList() ([]string, error) { return () } type Controller struct { Service *Service } func NewController(service *Service) *Controller { return &Controller{Service: service} } func (c *Controller) GetItemList() ([]string, error) { return () } var MegaSet = (NewDataSource, NewDao, NewService, NewController) func getItemList(w , r *) { controller := initializeController() itemList, _ := () output, _ := (itemList) (w, string(output)) } func main() { ("/items", getItemList) err := (":8080", nil) if err != nil { panic(err) } }
Key Code:
//The code before executing the wire commandfunc initializeController() *Controller { (MegaSet) return &Controller{} } //The generated code after execution// Injectors from : func initializeController() *Controller { dataSource := NewDataSource() dao := NewDao(dataSource) service := NewService(dao) controller := NewController(service) return controller }
By generating code to solve the problem of dependency injection, it can not only improve development efficiency but also not affect code performance. For more advanced wireless usage, you can check it in github document.
- tips: If an error is reported
other declaration of xxxx
, please add to the source file header//+build wireinject
- The go-zero framework also uses wire to solve DI problems
This is the end of this article about a brief analysis of golang's dependency injection. For more related go dependency injection content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!