Categorygithub.com/alecthomas/inject
modulepackage
0.0.0-20180408235619-a9441b76e57f
Repository: https://github.com/alecthomas/inject.git
Documentation: pkg.go.dev

# README

Inject - Guice-ish dependency-injection for Go.

Build Status Gitter chat

Inject provides dependency injection for Go. For small Go applications, manually constructing all required objects is more than sufficient. But for large, modular code bases, dependency injection can alleviate a lot of boilerplate.

In particular, Inject provides a simple way of wiring together modular applications. Each module contains configuration and logic to create the objects it provides. The main application installs all of these modules, then calls its main entry point using the injector. The injector resolves any dependencies of the main function and injects them.

Example usage

The following example illustrates a simple modular application.

First, the main package installs configured modules and calls an entry point:

package main

func run(db *mgo.Database, log *log.Logger) {
  log.Println("starting application")
  // ...
}

func main() {
  injector := New()
  injector.Install(
    &MongoModule{URI: "mongodb://db1.example.net,db2.example.net:2500/?replicaSet=test&connectTimeoutMS=300000"""},
    &LoggingModule{Flags: log.Ldate | log.Ltime | log.Llongfile},
  )
  injector.Call(run)
}

Next we have a simple Mongo module with a configurable URI:

package db

type MongoModule struct {
  URI string
}

func (m *MongoModule) ProvideMongoDB() (*mgo.Database, error) {
  return mgo.Dial(m.URI)
}

The logging package shows idiomatic use of inject; it is just a thin wrapper around normal Go constructors. This is the least invasive way of using injection, and preferred.

package logging

// LoggingModule provides a *log.Logger that writes log lines to a Mongo collection.
type LoggingModule struct {
  Flags int
}

func (l *LoggingModule) ProvideMongoLogger(db *mgo.Database) *log.Logger {
  return NewMongoLogger(db, l.Flags)
}

type logEntry struct {
  Text string `bson:"text"`
}

func NewMongoLogger(db *mgo.Database, flags int) *log.Logger {
  return log.New(&mongologWriter{c: db.C("logs")}, "", flags)
}

type mongoLogWriter struct {
  buf string
  c *mgo.Collection
}

func (m *mongoLogWriter) Write(b []byte) (int, error) {
  m.buf = m.buf + string(b)
  for {
    eol := strings.Index(m.buf, "\n")
    if eol == -1 {
      return len(b), nil
    }
    line := m.buf[:eol]
    err := m.c.Insert(&logEntry{line})
    if err != nil {
      return len(b), err
    }
    m.buf = m.buf[eol:]
  }
}

Value bindings

The simplest form of binding simply binds a value directly:

injector.Bind(http.DefaultServeMux)

Singletons

Function bindings are not singleton by default. For example, the following function binding will be called each time an int is requested:

value := 0
injector.Bind(func() int {
  value++
  return value
})

Wrap the function in Singleton() to ensure it is called only once:

injector.Bind(Singleton(func() int {
  return 10
}))

Literals

To bind a function as a value, use Literal:

injector.Bind(Literal(fmt.Sprintf))

Mapping bindings

Mappings can be bound explicitly:

injector.Bind(Mapping(map[string]int{"one": 1}))
injector.Bind(Mapping(map[string]int{"two": 2}))
injector.Bind(Mapping(func() map[string]int { return map[string]int{"three": 3} }))
injector.Call(func(m map[string]int) {
  // m == map[string]int{"one": 1, "two": 2, "three": 3}
})

Or provided via a Provider method that includes the term Mapping in its name:

func (m *MyModule) ProvideStringIntMapping() map[string]int {
  return map[string]int{"one": 1, "two": 2}
}

Sequence bindings

Sequences can be bound explicitly:

injector.Bind(Sequence([]int{1, 2}))
injector.Bind(Sequence([]int{3, 4}))
injector.Bind(Sequence(func() []int { return  []int{5, 6} }))
injector.Call(func(s []int) {
  // s = []int{1, 2, 3, 4, 5, 6}
})

Or provided via a Provider method that includes the term Sequence in its name:

func (m *MyModule) ProvideIntSequence() []int {
  return []int{1, 2}
}

Named bindings

The equivalent of "named" values can be achieved with type aliases:

type UserName string

injector.Bind(UserName("Bob"))
injector.Call(func (username UserName) {})

Interfaces

Interfaces can be explicitly bound to implementations:

type stringer string
func (s stringer) String() string { return string(s) }

injector.BindTo((*fmt.Stringer)(nil), stringer("hello"))
injector.Call(func(s fmt.Stringer) {
  fmt.Println(s.String())
})

However, if an explicit interface binding is not present, any bound object implementing that interface will be used:

injector.Bind(stringer("hello"))
injector.Call(func(s fmt.Stringer) {
  fmt.Println(s.String())
})

Similarly, if sequences/maps of interfaces are injected, explicit bindings will be used first, then inject will fallback to sequences/maps of objects implementing that interface.

Modules

Similar to injection frameworks in other languages, inject includes the concept of modules. A module is a struct whose methods are providers. This is useful for grouping configuration data together with providers.

Any method starting with "Provide" will be bound as a Provider. If the method name contains "Multi" it will not be a singleton provider. If the method name contains "Sequence" it will contribute to a sequence of its return type. Similarly, if the method name contains "Mapping" it will contribute to a mapping of its return type.

type MyModule struct {}

// Singleton provider.
func (m *MyModule) ProvideLog() *log.Logger { return log.New() }

type Randomness int

// "Multi" provider, called every time an "int" is injected.
func (m *MyModule) ProvideMultiRandomness() Randomness { return Randomness(rand.Int()) }

Validation

Finally, after binding all of your types to the injector you can validate that a function is constructible via the Injector by calling Validate(f).

Or you can live on the edge and simply use Call(f) which will panic if injection is not possible.

# Functions

Annotate ensures that v is an annotation, and returns it.
Literal annotates a value as being provided as-is with no further transformation.
Mapping annotates a provider or binding to indicate it is part of a mapping of keys to values.
New creates a new Injector.
Provider annotates a function to indicate it should be called whenever the type of its return value is requested.
SafeNew creates a new SafeInjector.
Sequence annotates a provider or binding to indicate it is part of a slice of values implementing the given type.
Singleton annotates a provider function to indicate that the provider will only be called once, and that its return value will be used for all subsequent retrievals of the given type.

# Structs

Binding represents a function that resolves to a value given a set of input values.
SafeInjector is an IoC container.
SafeInjector is an IoC container.

# Interfaces

An Annotation modifies how a type is built and retrieved from the SafeInjector.
Binder is an interface allowing bindings to be added.
A Module implementing this interface will have its Configure() method called at Install() time.
No description provided by the author