Golang less than 30 lines of code to implement dependency injection

Source: Internet
Author: User

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)

    1. Read the dependency of an object by reflection (Golang is implemented by tag)
    2. Find the object instance in the container
    3. 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
    4. 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")}
    1. 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.
    2. Determine the type of Di tag to identify the injected singleton or prototype object

Test

    1. The Singleton object has only one instance in the entire container, so the pointer must be the same no matter where it is injected.
    2. 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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.