Documentation ¶
Overview ¶
Package ioc provides inversion of control containers and functionality.
The ioc.Container and ioc.Values structs implement the Factory interface.
Basics ¶
The containers provided by package ioc make use of runtime reflection to detect value types and to resolve an instance by type and name.
Containers can be scoped.
Example:
type UserRepository interface { GetById(int64 id) (*User, error) } type PostgresUserRepository struct { db *sql.DB } func newPostgresUserRepository(db *sql.DB) *PostgresUserRepository { return &PostgresUserRepository{db: db} } func (repo *PostgresUserRepository) GetById(int64 id) (*User, error) { return nil, nil // stub } c := ioc.NewContainer() // register PostgresUserRepository as UserRepository createInstance := func(factory ioc.Factory) (interface{}, error) { var db *sql.DB // Resolve requires a non-nil pointer, but you can pass a reference to a nil pointer. if err := ioc.Resolve(factory, &db); err != nil { return nil, err } repo := newPostgresUserRepository(db) return repo, nil } implType := (*UserRepository)(nil) // must be a nil pointer lifetime := ioc.PerContainer c.MustRegister(createInstance, implType, lifetime) // register the singleton *sql.DB driverName := "postgres" dataSourceName := "mydb" db, err := sql.Open(driverName, dataSourceName) if err != nil { panic(err) } c.MustRegisterInstance(db) // the instance being registered can't be a nil pointer or interface. // tell the container to construct a UserRepository var userRepository UserRepository // Resolve requires a non-nil pointer, but you can pass a reference to a nil pointer. c.MustResolve(&userRepository) // scoped example func ContainerMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) { scopedContainer := c.Scope() scopedContainer.MustSet(&w) scopedContainer.MustSet(r) // use the scopedContainer within the request scope, e.g. using gorilla context.Set(r, "container", scopedContainer) }) } func DoSomething(w http.ResponseWriter, r *http.Request) { container := context.Get(r, "container").(*Container) var userRepository UserRepository container.MustGet(&userRepository) user, err := userRepository.GetById(1) }
Resolving Instances ¶
The following methods can be used to resolve instances:
- (*ioc.Values) Get/GetNamed
- (ioc.Factory) ResolveNamed
- ioc.Resolve/ioc.ResolveNamed
Resolved instances are stored in the value pointed to by v.
A non-nil pointer or a reference to a nil-pointer is required to set the value pointed to by v.
Registering Instances ¶
The following methods can be used to register instances:
(*ioc.Values) Set/SetNamed
(*ioc.Container) Set/SetNamed (scoped container singleton/scope vars)
(*ioc.Container) RegisterInstance/RegisterNamedInstance (root container singleton)
The instance being registered can't be a nil pointer or interface.
As a best practice, always pass the pointer to the instance you want to register.
Example: Register an instance of an interface type
values := NewValues() file, _ := os.Open("my_file") // file: *os.File defer file.Close() var f io.Reader = file values.Set(f) // type registered: *os.File (wrong!) values.Set(&f) // type registered: io.Reader
Instance Factory Registrations ¶
The following methods can be used to register an instance factory:
- (*ioc.Container) Register/RegisterNamed (instance factory)
An instance factory function must return a non-nil value or an error.
The value type should match the implementing type or the implementing type must be an interface and the value must implement that interface.
The Lifetime characteristics determines how a resolved instance is reused:
- Per Container Lifetime requires that an instance is only created once per container.
- Per Scope lifetime requires that an instance is only created once per scope.
- Per Request lifetime requires that a new instance is created on every request.
Index ¶
- Variables
- func GetNamedInstance(v interface{}, name string) (*reflect.Value, error)
- func GetNamedSetter(v interface{}, name string) (*reflect.Value, error)
- func GetNamedType(v interface{}, name string) (reflect.Type, error)
- func MustResolve(factory Factory, instances ...interface{})
- func MustResolveNamed(factory Factory, namedInstances map[string][]interface{})
- func Resolve(factory Factory, instances ...interface{}) error
- func ResolveNamed(factory Factory, namedInstances map[string][]interface{}) error
- type Container
- func (c *Container) MustRegister(createInstance func(Factory) (interface{}, error), implType interface{}, ...)
- func (c *Container) MustRegisterInstance(v interface{})
- func (c *Container) MustRegisterNamed(createInstance func(Factory) (interface{}, error), implType interface{}, ...)
- func (c *Container) MustRegisterNamedInstance(v interface{}, name string)
- func (c *Container) MustResolve(v interface{})
- func (c *Container) MustResolveNamed(v interface{}, name string)
- func (c *Container) Register(createInstance func(Factory) (interface{}, error), implType interface{}, ...) error
- func (c *Container) RegisterInstance(v interface{}) error
- func (c *Container) RegisterNamed(createInstance func(Factory) (interface{}, error), implType interface{}, ...) error
- func (c *Container) RegisterNamedInstance(v interface{}, name string) error
- func (c *Container) Registrations() []*Registration
- func (c *Container) Resolve(v interface{}) error
- func (c *Container) ResolveNamed(v interface{}, name string) error
- func (c *Container) Scope() *Container
- type Error
- type ErrorCode
- type Factory
- type Lifetime
- type Registration
- type Values
- func (values *Values) Get(v interface{}) error
- func (values *Values) GetNamed(v interface{}, name string) error
- func (values *Values) MustGet(v interface{})
- func (values *Values) MustGetNamed(v interface{}, name string)
- func (values *Values) MustSet(v interface{})
- func (values *Values) MustSetNamed(v interface{}, name string)
- func (values *Values) ResolveNamed(v interface{}, name string) error
- func (values *Values) Set(v interface{}) error
- func (values *Values) SetNamed(v interface{}, name string) error
Constants ¶
This section is empty.
Variables ¶
var RecursionLimit int = 30
RecursionLimit specifies the maximum count resolve can be called for a type and name before an error is raised, to avoid infinite recursion.
A limit of 1 is sufficient for detecting infinite recursion for the Per Container and Per Scope lifetimes.
An (infinite -1) limit is required for detecting infinite recursion for the Per Request lifetime.
Due to the infinite limit required to accurately detect infinite recursion for the Per Request lifetime, you should set the RecursionLimit to a reasonable setting.
Functions ¶
func GetNamedInstance ¶
Get the non-pointer reflect.Value of v.
GetNamedInstance is used to get value and type information for storing singleton instances on the Container.
Returns an error when:
- The value type is nil. (v was passed as nil with no type information)
- The value is a nil pointer or interface.
func GetNamedSetter ¶
Get the reflect.Value that can be used to set the value of v.
Returns an error when:
- The value type is nil. (v was passed as nil with no type information)
- The value isn't a pointer. (required to set v to the instance)
- The value is a nil pointer which can't be set. (use a pointer to a (nil) pointer instead)
func GetNamedType ¶
Get the non-pointer reflect.Type of v.
GetNamedType is used to get the type information of implementation types.
Returns an error when:
- The value type is nil. (v was passed as nil with no type information)
- The value isn't a pointer. (enforced rule to ensure Interface types are registered properly)
func MustResolve ¶
func MustResolve(factory Factory, instances ...interface{})
MustResolve uses a factory to resolve instances by type.
MustResolve calls Resolve(factory, instances...) and panics if an error is returned.
func MustResolveNamed ¶
MustResolveNamed uses a factory to resolve instances by type and name.
MustResolveNamed calls ResolveNamed(factory, namedInstances) and panics if an error is returned.
func ResolveNamed ¶
ResolveNamed uses a factory to resolve instances by type and name.
Types ¶
type Container ¶
type Container struct { *Values // contains filtered or unexported fields }
Container is an inversion of control container.
func NewContainer ¶
func NewContainer() *Container
NewContainer creates a new inversion of control container.
func (*Container) MustRegister ¶
func (c *Container) MustRegister(createInstance func(Factory) (interface{}, error), implType interface{}, lifetime Lifetime)
Register an instance factory with a specific lifetime.
MustRegister calls Register(createInstance, implType, lifetime) and panics if an error is returned.
func (*Container) MustRegisterInstance ¶
func (c *Container) MustRegisterInstance(v interface{})
Register an instance on the root container.
MustRegisterInstance calls RegisterInstance(v) and panics if an error is returned.
func (*Container) MustRegisterNamed ¶
func (c *Container) MustRegisterNamed(createInstance func(Factory) (interface{}, error), implType interface{}, name string, lifetime Lifetime)
Register a named instance factory with a specific lifetime.
MustRegisterNamed calls RegisterNamed(createInstance, implType, name, lifetime) and panics if an error is returned.
func (*Container) MustRegisterNamedInstance ¶
Register a named instance on the root container.
MustRegisterNamedInstance calls RegisterNamedInstance(v, name) and panics if an error is returned.
func (*Container) MustResolve ¶
func (c *Container) MustResolve(v interface{})
Resolve an instance by type.
MustResolve calls Resolve(v) and panics if an error is returned.
func (*Container) MustResolveNamed ¶
Resolve a named instance by type.
MustResolveNamed calls ResolveNamed and panics if an error is returned.
func (*Container) Register ¶
func (c *Container) Register(createInstance func(Factory) (interface{}, error), implType interface{}, lifetime Lifetime) error
Register an instance factory with a specific lifetime.
Register calls RegisterNamed(createInstance, implType, "", lifetime).
func (*Container) RegisterInstance ¶
Register an instance on the root container.
RegisterInstance calls RegisterNamedInstance(v, "").
func (*Container) RegisterNamed ¶
func (c *Container) RegisterNamed(createInstance func(Factory) (interface{}, error), implType interface{}, name string, lifetime Lifetime) error
Register a named instance factory with a specific lifetime.
Returns an error when:
- The factory function is nil. (createInstance)
- The implementing type is nil.
- The implementing type isn't a pointer.
- The instance lifetime isn't supported. Currently only PerContainer, PerScope and PerRequest lifetimes are supported.
func (*Container) RegisterNamedInstance ¶
Register a named instance on the root container.
Returns an error when:
- The instance type is nil.
- The instance is a nil pointer or interface.
func (*Container) Registrations ¶
func (c *Container) Registrations() []*Registration
Returns the registrations for the container.
func (*Container) ResolveNamed ¶
Resolve a named instance by type.
ResolveNamed creates a dependency resolver implementing the Factory interface, that proxies resolve calls to the Container.
The dependency resolver is passed to instance factory functions (instead of the container) and keeps track of the resolve call history for the request to detect infinite recursion.
Returns an error when:
- The value type is nil.
- The value isn't a pointer.
- The value is a nil pointer e.g. (*string)(nil) (use a pointer to a (nil) pointer instead)
- The dependency can't be resolved (not registered).
- The instance lifetime isn't supported. Currently only PerContainer, PerScope and PerRequest lifetimes are supported.
- An error was returned when (*Registration).CreateInstance was called.
- Infinite recursion is detected on a repetitive call to resolve an instance by type and name.
func (*Container) Scope ¶
Scope creates a new scoped container from the current container.
The Values of the current container are scoped and the registry inherited by the scoped container.
Scoped Values will resolve an instance from an ancestor when the current container is unable to resolve the instance by type and name.
type Error ¶
type ErrorCode ¶
type ErrorCode int
ErrorCode represents an error code for distinguishing between errors.
const ( // ErrInstanceNotFound is raised by (*Values).GetNamed when an instance isn't found. ErrInstanceNotFound ErrorCode = iota // ErrNilType is raised by GetNamedSetter, GetNamedInstance, GetNamedType when the type of v is nil. // (e.g. called GetNamedType(v:nil, name:""). ErrNilType // ErrCreateInstanceNil is raised // by (*Container).RegisterNamed when createInstance is nil or // by (*Registration).CreateInstance when (*Registration).CreateInstanceFn is nil. ErrCreateInstanceNil // ErrCreateInstance is raised by (*Registration).CreateInstance when (*Registration).CreateInstanceFn // is not nil and returned an error. ErrCreateInstance // ErrUnresolvedDependency is raised by by (*dependencyResolver).ResolveNamed // when an instance isn't registered as a singleton or a factory function // and an instance can't be found on (*Container).Values. ErrUnresolvedDependency // ErrUnsupportedLifetime is raised by (*Container).RegisterNamed, (*Registration).CreateInstance // when the lifetime isn't supported. ErrUnsupportedLifetime // ErrUnexpectedValueType is raised by (*Registration).CreateInstance // when the type of the created instance doesn't match the registration type. ErrUnexpectedValueType // ErrInterfaceNotImplemented is raised by (*Registration).CreateInstance // when the registration type is an interface and the created instance type // doesn't implement the interface. ErrInterfaceNotImplemented // ErrNilValue is raised by GetNamedInstance when v is nil or a pointer to a nil value. ErrNilValue // ErrNonSetNilPointer is raised by GetNamedSetter when v is a nil pointer. ErrNonSetNilPointer // ErrRequirePointer is raised by GetNamedSetter, GetNamedType when v isn't a pointer. ErrRequirePointer // ErrResolveInfiniteRecursion is raised by (*dependencyResolver).ResolveNamed // when the count of resolve by type and name within a (*Container).ResolveNamed call // exceeds the RecursionLimit. ErrResolveInfiniteRecursion )
type Factory ¶
type Factory interface { // Resolve a named instance by type. ResolveNamed(v interface{}, name string) error }
Factory represents a container able to resolve instances by type and name.
Implemented by:
- Values
- Container
- dependencyResolver (internal)
type Lifetime ¶
type Lifetime int
Lifetime represents the lifetime characteristics of an instance.
const ( // Per Container Lifetime requires that an instance is only created once per container. PerContainer Lifetime = iota // Per Scope lifetime requires that an instance is only created once per scope. PerScope // Per Request lifetime requires that a new instance is created on every request. PerRequest )
type Registration ¶
type Registration struct { Type reflect.Type Name string Value interface{} CreateInstanceFn func(Factory) (interface{}, error) Lifetime Lifetime }
Registration contains the information necessary to construct an instance.
func (*Registration) CreateInstance ¶
func (r *Registration) CreateInstance(factory Factory) (*reflect.Value, error)
CreateInstance creates an instance using the factory function.
Returns an error when:
- The factory function is nil or returns an error. (Registration.CreateInstanceFn)
- The created instance type is nil. (no type information)
- The created instance is a nil pointer or interface.
- The created instance type doesn't match the registration type or
- The implementation type is an interface and the created instance doesn't implement the interface.
type Values ¶
type Values struct {
// contains filtered or unexported fields
}
Values is a thread safe type-name-instance container.
func NewValuesScope ¶
NewValuesScope creates a new scoped Values struct with a reference to a parent Values struct.
Get calls will check the ancestors to resolve the instance by type and name.
func (*Values) GetNamed ¶
Get a named instance by type.
GetNamed calls GetNamedSetter(v, name).
Returns an error when:
- The value type is nil. (v was passed as nil with no type information)
- The value isn't a pointer. (required to set v to the instance)
- The value is a nil pointer which can't be set. (use a pointer to a (nil) pointer instead)
- The instance isn't found.
func (*Values) MustGet ¶
func (values *Values) MustGet(v interface{})
Get an instance by type.
MustGet calls Get(v) and panics if an error is returned.
func (*Values) MustGetNamed ¶
Get a named instance by type.
MustGetNamed calls GetNamed(v, name) and panics if an error is returned.
func (*Values) MustSet ¶
func (values *Values) MustSet(v interface{})
Set an instance by type.
MustSet calls Set(v) and panics if an error is returned.
func (*Values) MustSetNamed ¶
Set a named instance by type.
MustSetNamed calls SetNamed(v, name) and panics if an error is returned.
func (*Values) ResolveNamed ¶
Resolve a named instance by type.
ResolveNamed calls GetNamed(v, name).