当前位置:网站首页>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
边栏推荐
- Vofa+ software usage record
- Void* C is a carrier for realizing polymorphism
- 实例008:九九乘法表
- MySQL MHA high availability cluster
- Anonymous structure in C language
- [NAS1](2021CVPR)AttentiveNAS: Improving Neural Architecture Search via Attentive Sampling (未完)
- Use indent to format code
- Bluetooth hc-05 pairing process and precautions
- Several important parameters of LDO circuit design and type selection
- Hardware and software solution of FPGA key chattering elimination
猜你喜欢
Briefly talk about the identification protocol of mobile port -bc1.2
Carrier period, electrical speed, carrier period variation
Solutions to compilation warnings in Quartus II
实例004:这天第几天 输入某年某月某日,判断这一天是这一年的第几天?
How to copy formatted notepad++ text?
剑指 Offer 09. 用两个栈实现队列
STM32 single chip microcomputer -- volatile keyword
Charge pump boost principle - this article will give you a simple understanding
Measurement fitting based on Halcon learning [i] fuse Hdev routine
Example 002: the bonus paid by the "individual income tax calculation" enterprise is based on the profit commission. When the profit (I) is less than or equal to 100000 yuan, the bonus can be increase
随机推荐
Detailed summary of FIO test hard disk performance parameters and examples (with source code)
matlab timeserise
Brief discussion on Buck buck circuit
剑指 Offer 09. 用两个栈实现队列
[tutorial 19 of trio basic from introduction to proficiency] detailed introduction of trio as a slave station connecting to the third-party bus (anybus PROFIBUS DP...)
Void* C is a carrier for realizing polymorphism
PMSM dead time compensation
[nas1] (2021cvpr) attentivenas: improving neural architecture search via attentive sampling (unfinished)
[trio basic tutorial 17 from getting started to mastering] set up and connect the trio motion controller and input the activation code
Sword finger offer 05 Replace spaces
The firmware of the connected j-link does not support the following memory access
Sword finger offer 06 Print linked list from end to end
Detailed explanation of SQL server stored procedures
实例003:完全平方数 一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
Example 004: for the day of the day, enter a day of a month of a year to judge the day of the year?
Synchronization of QT multithreading
Sword finger offer 09 Implementing queues with two stacks
Ble encryption details
Array integration initialization (C language)
关于线性稳压器的五个设计细节