Project Address
Go-di-demo
This project relies on
Using standard library implementations with no additional dependencies
Benefits of Dependency Injection
People with Java must not be unfamiliar with the spring framework, the spring core is an IOC (control inversion/Dependency injection) container, which brings a great advantage to decoupling. Generally rely only on the container, not on the specific class, when your class has modified, you need to change the container-related code, business code is not affected.
Golang's Dependency Injection principle
In general, similar to Java, the steps are as follows: (Golang does not support dynamic creation of objects, so you need to manually create objects and then inject, Java can directly create objects dynamically)
- Read the dependency of an object by reflection (Golang is implemented by tag)
- Find the object instance in the container
- If there is an object instance or a factory method that creates the object, either inject the object or use the factory to create the object and inject
- If there is no instance of the object, the error
Code implementation
A typical container is implemented as follows, with dependency types referencing spring's singleton/prototype, individual object and instance objects:
Package Diimport ("Sync" "Reflect" "FMT" "Strings" "Errors") var (errfactorynotfound = errors. New ("Factory not Found")) Type factory = Func () (interface{}, error)//container type Container struct {sync. Mutex singletons map[string]interface{} Factories map[string]factory}//container instantiation func newcontainer () *container {RE Turn &container{singletons:make (map[string]interface{}), Factories:make (map[string]factory),}}// Register the Singleton object func (P *container) Setsingleton (name string, Singleton interface{}) {P.lock () p.singletons[name] = Singleto n P.unlock ()}//Gets the Singleton object func (P *container) Getsingleton (name string) interface{} {return p.singletons[name]}//get instance pair Like Func (P *container) getprototype (name string) (interface{}, error) {factory, OK: = P.factories[name] If!ok { return nil, Errfactorynotfound} return factory ()}//set Instance Object factory func (P *container) setprototype (name string, factor Y Factory) {P.lock () p.factories[name] = FACTory P.unlock ()}//injection dependent func (P *container) ensure (instance interface{}) error {elemtype: = reflect. TypeOf (instance). Elem () Ele: = reflect. ValueOf (instance). Elem () for I: = 0; I < Elemtype.numfield (); i++ {//traversal field FieldType: = Elemtype.field (i) Tag: = FieldType.Tag.Get ("Di")//Get tag diname: = P.inje Ctname (tag) if diname = = "" {continue} var (diinstance interface{} Err error) if P.issingleton (tag) {diinstance = P.getsingleton (diname)} If P.isprototype (tag) {diinstance, err = P.getprototype (diname)} if Err! = Nil {ret Urn Err} if diinstance = = Nil {return errors. New (Diname + "dependency not Found")} ele. Field (i). Set (reflect. ValueOf (diinstance)} return nil}//gets the dependency name that needs to be injected func (P *container) injectname (tag String) string {Tags: = Strin Gs. Split (Tag, ",") IF len (tags) = = 0 {return ""} return tags[0]}//detect if a singleton relies on func (P *container) Issingleton (tag string) bool { Tags: = strings. Split (Tag, ",") for _, Name: = Range Tags {if name = = "Prototype" {return false}} re Turn true}//detects if the instance relies on Func (P *container) isprototype (tag string) bool {Tags: = strings. Split (Tag, ",") for _, Name: = Range Tags {if name = = "Prototype" {return true}} RET Urn false}//print Container internal instance func (P *container) string () string {lines: = make ([]string, 0, Len (p.singletons) +len (p.factories ) +2) lines = append (lines, "singletons:") for name, item: = Range P.singletons {line: = FMT. Sprintf ("%s:%x%s", name, &item, reflect. TypeOf (item). String ()) lines = append (lines, line)} lines = Append (lines, "factories:") for name, item: = Range P.fact ories {line: = FMT. Sprintf ("%s:%x%s", name, &item, reflect. TypeOf (item). String ()) lines = aPpend (lines, line)} return strings. Join (lines, "\ n")}
- Most importantly
Ensure , the method scans all the export fields of the instance and reads the DI tag, and if there is a tag, initiates the injection.
- Determine the type of Di tag to identify the injected singleton or prototype object
Test
- The Singleton object has only one instance in the entire container, so the pointer must be the same no matter where it is injected.
- Instance objects are created by the same factory method, so pointers to each instance cannot be the same.
Here is the test entry code, full code in the GitHub repository, interested to be able to flip through:
package mainimport ( "di" "database/sql" "fmt" "os" _ "github.com/go-sql-driver/mysql" "demo")func main() { container := di.NewContainer() db, err := sql.Open("mysql", "root:root@tcp(localhost)/sampledb") if err != nil { fmt.Printf("error: %s\n", err.Error()) os.Exit(1) } container.SetSingleton("db", db) container.SetPrototype("b", func() (interface{}, error) { return demo.NewB(), nil }) a := demo.NewA() if err := container.Ensure(a); err != nil { fmt.Println(err) return } // 打印指针,确保单例和实例的指针地址 fmt.Printf("db: %p\ndb1: %p\nb: %p\nb1: %p\n", a.Db, a.Db1, &a.B, &a.B1)}
The results printed after execution are:
db: 0xc4200b6140db1: 0xc4200b6140b: 0xc4200a0330b1: 0xc4200a0338
You can see a pointer of two DB instances, stating that the same instance is the same, and that the two B pointers are different, stating that it is not an instance.
Written in the last
Dependency Injection provides a good management of instantiation and dependencies between multiple objects, and a mate configuration file registers the instances that need to be injected into the container at the application initialization stage, and only needs to inject the container at the time of instantiation at any place in the application. No additional dependencies.
Personal blog
Www.ddhigh.com