当前位置:网站首页>Go dependency injection -- Google open source library wire
Go dependency injection -- Google open source library wire
2022-07-05 08:22:00 【CK continues to grow】
Catalog
If used java Your little partner must be injecting dependency ( dependency injection) Familiar with the , Dependency injection is a standard technology , By explicitly providing components with all the dependencies they need to work , To generate flexible and loosely coupled code . There are many dependency injection frameworks ,go Language anti haze , There is Uber Of dig and Facebook Of inject Both use reflection for runtime dependency injection .Wire Mainly by Java Dagger 2 Inspired by the , Use code generation instead of reflection or service locator .
Today we mainly talk about go Dependency injection tool in google Of wire.wire yes google Open source library go-cloud The dependency injection tool used
1. wire The benefits of using
stay Go in , This usually takes the form of passing dependencies to constructors :
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
This way of passing depends on the constructor , It works well when the application is small , If in a large application , Dependency diagrams can be complex , When a lot of initialization code depends on the dependency order of the constructor , At this time, it is not so easy to deal with . And when a dependency , To be dependent on multiple constructors , Each has to create objects manually , Afferent structure , It seems that the code is not so neat . Another problem is , If in a complex dependency graph , When a service is to be replaced by another service, it will be a headache . Because we need to be in a complex dependency graph , Find all classes that rely on old Services , Then replace it with a new service constructed , This kind of modification of initialization code is cumbersome and slow .
While using wire The advantage of relying on injection tools to manage initialization code is presented here . We can describe services and their dependencies as code or configuration , then wire The dependency graph will be automatically constructed , Then pass the dependencies needed to construct the service for each service . When the requirement modifies the dependency signature or deletes the dependency of the added service , Just modify the corresponding code and configuration ,wire It will automatically complete the injection of these service dependencies for us .
wire Several advantages given by the official :
- When the dependency graph becomes complex , Runtime dependency injection can be difficult to track and debug . Using code generation means that the initialization code executed at run time is routine 、 conventional Go Code , Easy to understand and debug . Nothing can be done by one “ Magic ” Is confused by the intervention framework . especially , Problems like forgetting dependencies become compile time errors , Not runtime errors .
- Different from the service locator , The registration service does not need to write any name or key .Wire Use Go Types connect components to their dependencies .
- It's easier to avoid dependency inflation .Wire The generated code will only import the dependencies you need , Therefore, your binaries will not have unused imports . The runtime dependency injector does not recognize unused dependencies until runtime .
- Wire The dependency graph of is statically knowable , This provides opportunities for tools and visualization .
2. install wire Tools
install wire It needs to be installed before go We won't go into details here .
install wire Tools
go install github.com/google/wire/cmd/[email protected]
You can go to $GOPATH/bin Look at the table of contents , Make sure wire The installation to bin The execution directory of .
3. wire How does it work
Wire There are two basic concepts : Provider (providers) and injector (injectors).
Provider (providers) It's a common go function , It provides the value of a given dependency , These values can be simply described as function parameters . Next, we define three program sample codes with dependencies :
// Generate UserStore object , Depends on configuration Config and DB
func NewUserStore(cfg *Config, db *gorm.DB) (*UserStore, error) {...}
// Provided Config Generate , There are no dependent parameters
func NewDefaultConfig() *Config {...}
// Provide build DB Example , Simultaneous dependence ConnectionInfo Parameters
func NewDB(info *ConnectionInfo) (*gorm.DB, error) {...}
Here, the initialization function requires the instance return value to provide the values that other functions depend on , That is, the dependent provider .
injector (injectors) Is to call the generator function of the provider in dependent order . You write the signature of the injector , Include any required inputs as parameters , And insert pair wire Call to . Build with the list of providers or provider sets needed to build the final result :
var UserStoreSet = wire.NewSet(NewUserStore, NewDefaultConfig)
wire Provide function signatures that will need to be relied on , Build a list of provider assemblies as parameters . such wire The assemblies can be provided in the order , To actually build the function initialization process code .
Finally through wire.go Of build Function provides external calling function , Generate actual code :
func InitUserStore(info *ConnectionInfo) (*UserStore, error) {
wire.Build(UserStoreSet, NewDB)
return nil, nil
}
And then in wire.go Under the directory of wire command :
wire
perform wire After the instruction , stay wire.go The directory of will generate a wire_gen.go The file of , When we open this file, we can see wire What did you do for us
// Code generated by Wire. DO NOT EDIT.
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package user
// Injectors from wire.go:
func InitUserStore(info *ConnectionInfo) (*UserStore, error) {
config := NewDefaultConfig()
db, err := NewDB(info)
if err != nil {
return nil, err
}
userStore, err := NewUserStore(config, db)
if err != nil {
return nil, err
}
return userStore, nil
}
I can see wire According to our wire.go Initialization function signature in , According to the actual construction order , Generates the same initialization function that actually initializes each dependency . Directly call the initialization constructor externally .
This is a simple example with only three components , So writing initializers by hand won't be too painful , but Wire It saves a lot of manual work for components and applications with more complex dependency diagrams .
4. How do we use it wire
According to the above wire Introduction to wire Is how it works , We have a general understanding of wire How to use , Now let's look at the implementation code .
First, we will build the corresponding structure according to the above example :
// user_service.go
package user
import (
"github.com/google/wire"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// Configuration class
type Config struct {
}
// Back to the user UserStore object , It is actually returned by querying data model
type UserStore struct {
Name string
Age int32
}
// structure UserStore Method , It should be db Query data
func NewUserStore(cfg *Config, db *gorm.DB) (*UserStore, error) {
// todo from db Query data from
return &UserStore{}, nil
}
// Default construction Config Methods
func NewDefaultConfig() *Config {
return &Config{}
}
// Database connection information structure
type ConnectionInfo struct {
Driver string
Source string
}
// DB Construction object of
func NewDB(info *ConnectionInfo) (*gorm.DB, error) {
return gorm.Open(mysql.Open(info.Source))
}
// wire Construction provided Set Set
var UserStoreSet = wire.NewSet(NewUserStore, NewDefaultConfig)
Let's implement wire.go The file of , The function used to generate the corresponding call
// +build wireinject
package user
import "github.com/google/wire"
func InitUserStore(info *ConnectionInfo) (*UserStore, error) {
wire.Build(UserStoreSet, NewDB)
return nil, nil
}
wire Construction file of ,“ // +build wireinject” This has to be added with , Otherwise, it will be generated later wire_gen.go Your methods will conflict .
Actually implement wire After execution , Will generate wire.gen The file of
package user
// Injectors from wire.go:
func InitUserStore(info *ConnectionInfo) (*UserStore, error) {
config := NewDefaultConfig()
db, err := NewDB(info)
if err != nil {
return nil, err
}
userStore, err := NewUserStore(config, db)
if err != nil {
return nil, err
}
return userStore, nil
}
Then call it directly :
// main.go
func main() {
info := &user.ConnectionInfo{
Driver: "mysql",
Source: "root:[email protected](127.0.0.1:3333)/user?charset=utf8mb4&parseTime=True&loc=Local",
}
userStore, _ := user.InitUserStore(info)
log.Printf("user store result:%v\n", userStore)
}
5. wire Advanced features of
- ProviderSet Nested calls to
In the above example , Use wire.NewSet Tectonic Set Set as follows :
// wire Construction provided Set Set
var UserStoreSet = wire.NewSet(NewUserStore, NewDefaultConfig)
Here in addition to using the constructor in front , We can also pass in other default wire.NewSet Tectonic Set aggregate , Equivalent to that we can nest constructs . for example , If NewDefaultConfig It's also a wire.NewSet Constructed set , as follows :
// config.go
var ProviderConfigSet = wire.NetSet(NewConfig, NewOptions)
Then the above use wire structure UserStoreSet Of can be written as :
// wire Construction provided Set Set
var UserStoreSet = wire.NewSet(NewUserStore, ProviderConfigSet)
This nesting method can package multiple constructors with the same dependencies of multiple types into a collection , You only need to use this set later .
alike , stay wire.Build We can also directly pass in the corresponding UserStoreSet
- Use wire.Struct take Provider Inject struc in
type Foo int
type Bar int
func ProvideFoo() Foo {
return 1
}
func ProvideBar() Bar {
return 2
}
type FooBar struct {
MyFoo Foo
MyBar Bar
}
var Set = wire.NewSet(
ProvideFoo,
ProvideBar,
wire.Struct(new(FooBar), "MyFoo", "MyBar"))
have access to wire.Sturct structure , take FooBar Structure dependent MyFoo and MyBar Field , adopt ProvideFoo and ProvideBar The provided value is injected into FooBar structure , Through the following wire Construction file of , We can look at the specific implementation :
// wire.go
func CreateFoobar() (*FooBar, error) {
wire.Build(Set)
return nil, nil
}
Generated wire_gen.go file , You can see the concrete implementation :
// wire_gen.go
func CreateFoobar() (*FooBar, error) {
foo := ProvideFoo()
bar := ProvideBar()
fooBar := &FooBar{
MyFoo: foo,
MyBar: bar,
}
return fooBar, nil
}
- Use wire Of Value Bind injection value
Use wire.Value Bind an actual dependent value , In the following code , We can see through Value To construct a Foo struct Value .
type Foo struct {
X int
}
func injectFoo() Foo {
wire.Build(wire.Value(Foo{X: 42}))
return Foo{}
}
Use InterfaceValue Binding implementation interface Value
func CreateReader() io.Reader {
wire.Build(wire.InterfaceValue(new(io.Reader), os.Stdin))
return nil
}
take io.Reader The interface implementation of is bound to os.Stdin Input implementation , Used for construction io.Reader The dependencies of .
notes : wire The specific implementation code can be viewed by referring to the above
6. Reference material
wire Blog :https://go.dev/blog/wire
official guide: https://github.com/google/wire/blob/main/docs/guide.md
边栏推荐
- Shape template matching based on Halcon learning [VII] reuse_ model. Hdev routine
- 实例005:三数排序 输入三个整数x,y,z,请把这三个数由小到大输出。
- STM32 single chip microcomputer - bit band operation
- My-basic application 1: introduction to my-basic parser
- NTC thermistor application - temperature measurement
- 【三层架构及JDBC总结】
- 实例004:这天第几天 输入某年某月某日,判断这一天是这一年的第几天?
- STM32 single chip microcomputer - external interrupt
- Bluetooth hc-05 pairing process and precautions
- DokuWiki deployment notes
猜你喜欢
Negative pressure generation of buck-boost circuit
[three tier architecture]
[tutorial 15 of trio basic from introduction to proficiency] trio free serial communication
Management and use of DokuWiki (supplementary)
Brief discussion on Buck buck circuit
Step motor generates S-curve upper computer
Arduino uses nrf24l01+ communication
Shape template matching based on Halcon learning [vi] find_ mirror_ dies. Hdev routine
[trio basic tutorial 16 from introduction to proficiency] UDP communication test supplement
PMSM dead time compensation
随机推荐
My-basic application 2: my-basic installation and operation
MySQL之MHA高可用集群
99 multiplication table (C language)
Imx6ull bare metal development learning 2- use C language to light LED indicator
Arduino uses nrf24l01+ communication
C WinForm [view status bar -- statusstrip] - Practice 2
matlab timeserise
My-basic application 1: introduction to my-basic parser
Carrier period, electrical speed, carrier period variation
Measurement fitting based on Halcon learning [i] fuse Hdev routine
亿学学堂给的证券账户安不安全?哪里可以开户
[trio basic tutorial 16 from introduction to proficiency] UDP communication test supplement
Example 003: a complete square is an integer. It is a complete square after adding 100, and it is a complete square after adding 168. What is the number?
Summary of SIM card circuit knowledge
Count the number of inputs (C language)
关于线性稳压器的五个设计细节
DCDC circuit - function of bootstrap capacitor
Explain task scheduling based on Cortex-M3 in detail (Part 1)
STM32 outputs 1PPS with adjustable phase
Design a clock frequency division circuit that can be switched arbitrarily