di

package module
v0.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2024 License: MIT Imports: 9 Imported by: 0

README

di [WIP]

This is The Way to do Dependency injection in Go.


import "github.com/go-path/di"

type JediService interface {
    FeelTheForce()
}

type PadawanController struct {
    s JediService
}

func (p * PadawanController) Initialize() {
    p.s.FeelTheForce()
}

// register as startup component, injecting dependencies
di.Register(func(s JediService) *PadawanController {
	return &PadawanController{s:s}
}, di.Startup(100))


// (...) in a package far, far away ...

type yodaServiceImp struct {}

func (s * yodaServiceImp) FeelTheForce() {
    print("Patience you must have my young Padawan")
}

di.Register(&yodaServiceImp{})

// ... and
di.Initialize() 

This implementation has the following main objectives:

  • Easy to understand
  • Typesafe, using generics
  • Extensible and configurable
  • A foundation for the development of more complex frameworks and architectural structures.

ARE YOU ALLERGIC? ATTENTION!

Some concepts applied in this library may contain gluten and/or be inspired by the implementations of CDI Java and Spring IoC.

And obviously, this implementation uses reflection, which for some uninformed individuals can be detrimental. It is important to mention that we thoroughly sanitize our hands before handling any Type or Value.

We will not be held responsible if you become a productive developer after coming into contact with any part of this library.

"Your path you must decide.” — Yoda


Don't know what DI is? Wikipedia

The diagram below provides a summarized overview of how this DI container functions.

  • Container: The current implementation allows the developer to register and obtain their components.
  • Component: Any object (simple or complex) that the developer wishes to register in the container to be used by other (injected) components. During dependency injection, the injected constructor does not know how the requested component will be created. The container resolves the dependency, simplifying the development of complex systems.
  • Factory: It informs how an instance of a component can be created. The developer registers a component constructor or even an instance in the container, and the container performs the necessary processing to generate the factory for that component and resolve its dependencies when the constructor is invoked. It is possible to have more than one factory for the same type of component. The container ensures that the correct instance is obtained according to the rules described later in this document.
  • Scope: It manages the lifecycle of an instance. The developer specifies the scope of the component during registration. When the component is requested, the container asks the defined scope for the instance. The scope can return an existing instance or create a new one. This allows for the instantiation of components with various lifecycles; for example, instances that will be discarded at the end of a web request and other singleton instances that will remain alive until the entire application is terminated.

Diagram 1

Docs

Installation

go get github.com/go-path/di

Container

di.Container is owr IoC Container.

If necessary, you can instantiate new containers using the method New(parent Container) Container. We've already registered a global container and exposed all methods to simplify the library's usage.

Usually, you only need to interact with the method di.Register(ctor any, opts ...FactoryConfig) for component registration and finally the method di.Initialize(contexts ...context.Context) error for the container to initialize the components configured as 'Startup'.

When conducting unit tests in your project, take a look at method Mock(mock any) (cleanup func()).

If you're building a more complex architecture in your organization or a library with DI support, you'll likely use the methods below and others documented in the API to have full control over the components.

RegisterScope(name string, scope ScopeI) error

Get(key reflect.Type, ctx ...context.Context) (any, error)
Filter(options ...FactoryConfig) *FilteredFactories

Contains(key reflect.Type) bool

GetObjectFactory(factory *Factory, managed bool, ctx ...context.Context) CreateObjectFunc
GetObjectFactoryFor(key reflect.Type, managed bool, ctx ...context.Context) CreateObjectFunc
ResolveArgs(factory *Factory, ctx ...context.Context) ([]reflect.Value, error)
Destroy() error

DestroyObject(key reflect.Type, object any) error
DestroySingletons() error

Registrar Componentes e Serviços/Daemons

Nós registramos serviços usando o método di.Register(ctor any, opts ...FactoryConfig). NÃO ENTRE EM PÂNICO, mas esse método causa panic se houver algum erro no registro do componente. Isso acontece pois o esperado é que o registro de um componente não falhe. Se você precisar gerenciar o registro de um componente, utilize o método alternativo ShouldRegister(ctor any, opts ...FactoryConfig) error que retorna um erro.

O formato geral de registro de um componente é:

di.Register utilization

di.Register(
    func(a DependencyA, b DependencyB) MyComponent, error {
        return &myComponentImpl{a:a, b:b}
    }, 
    di.Primary, di.Order(1)
)

Caso o componente não precise de um construtor (true Singleton), utilize o padrão abaixo, retornando a instancia que será usada em todo o container.

di.Register(&myComponentImpl{}, di.Primary, di.Order(2))

Se você deseja registrar um serviço/daemon, utilize o padrão abaixo. Esse formato só é útil para serviços que vão ser inicializado junto com o container (configuração di.Startup(order))

di.Register(
    func(a DependencyA, b DependencyB) error {
        print("initialized")
    }, 
    di.Startup(100)
)
Component Type = KEY & Factory Configurations

O tipo do retorno da função construtora determina o tipo do seu componente. Essa é a chave usada internamente pelo container para identificar os objetos. Você pode registrar quantos candidatos desejar para o mesmo tipo de retorno, e usar as configurações da fábrica para ajustar a prioridade, o escopo ou até mesmo qualificar algum componente, permitindo que o container possa identificar o construtor mais adequado durante o processo de injeção.

Existe um capitulo mais abaixo descrevendo as Factory Configurations existentes o como elas podem ser usadas para permitir um desenvolvimento modular da sua aplicação.

Dependencias

Você pode usar qualquer tipo de objeto para identificar suas dependencias, porém o mais recomendado é seguir o Princípio da Inversão de Dependencia, utilizando as interface e permitindo que o container possa obter a instancia compatível. Isso reduz o acoplamento entre os modulos da sua aplicação, simplificando a manutenção e testes unitários.

O container aplica a seguintes regras de priorização para determinar as fábricas candidatas para criação da dependencia.

  1. Os componentes registrados com o mesmo tipo da dependencia (exact match)
  2. Os componentes registrados em que a dependecia solicitada é assignable com tipo do componente registrado (see Type.AssignableTo, Go Assignability)

Exemplo de declaraçao e uso de dependencia válida. Perceba que registramos o componente do tipo typeof *dependencyBImpl. Este componente é assignable com b DependencyB e c *dependencyBImpl.

type DependencyA interface { DoIt() }
type DependencyB interface { DoItBetter() }

type dependencyAImpl struct { /*...implements_A_DoIt()...*/ }
type dependencyBImpl struct { /*...implements_B_DoItBetter()...*/ }

// Component Type = Key = typeof DependencyA
di.Register(func() DependencyA { return &dependencyAImpl{} })

// Component Type = Key = typeof *dependencyBImpl
di.Register(func() dependencyBImpl { return &dependencyBImpl{} })

// a Key = typeof DependencyA, exact match (di.Register)
// b Key = typeof DependencyB, assignable match ('*testServiceBImpl' is a candidate for 'testServiceB')
// c Key = typeof *dependencyBImpl, exact match (di.Register)
di.Register(func(a DependencyA, b DependencyB, c *dependencyBImpl) {    
    // b == c
})

Já abaixo temos um exemplo inválido de dependencia, resultado no erro missing dependencies.

type DependencyA interface { DoIt() }

type dependencyAImpl struct { /*...implements_A_DoIt()...*/ }
func (* dependencyAImpl) DoAnotherThing() {}

type dependencyAImpl2 struct { /*...implements_A_DoIt()...*/ }

// Component Type = Key = typeof DependencyA
di.Register(func() DependencyA { return &dependencyAImpl2{} })

// d Key = typeof *dependencyAImpl
di.Register(func(d *dependencyAImpl) {    
   
})

Veja que apesar de termos declarado a existencia do componente do tipo DependencyA, e existir uma fábrica que retorna uma instancia que o tipo é assignable (d *dependencyAImpl2), o container não consegue saber, antes de invocar o construtor, se o tipo retornado é compatível, e neste caso não é. A nossa dependencia (d *dependencyAImpl) é assignable de DependencyA, mas também implementa outro método e poderia ser assignable de outros componentes que podem não ser satisfeito pelo contrutor existente.

Portanto, é importante que a dependencias sejam declaradas preferencialmente utilizando a interface do tipo, e não a implementação (SOLID - DIP).

Factory Configurations

O di disponibiliza algumas configurações sobre as fábricas de componentes, permitindo que o desenvolvedor possa configurar o ciclo de vida dos componentes ou mesmo gerar identificadores para permitir a obtenção de componentes que possuam algum marcador específico.

Initializer

Initializer register a initializer function to the component. A factory component may declare multiple initializers methods. If the factory returns nil, the initializer will be ignored

Disposer

Disposer register a disposal function to the component. A factory component may declare multiple disposer methods. If the factory returns nil, the disposer will be ignored

Startup

Startup indicates that this component must be initialized during container initialization (Container.Initialize method)

di.Register(func()  {
	print("Second")
}, Startup(200))

di.Register(func()  {
	print("First")
}, Startup(100)) 
Order

Order can be applied to any component to indicate in what order they should be used.

Higher values are interpreted as lower priority. As a consequence, the object with the lowest value has the highest priority.

Same order values will result in arbitrary sort positions for the affected objects.

If the component is marked as Startup, the order determines its execution order.

Order is also used during dependency injection. The candidate with the lower order will be injected.

A framework can implement filters and use order to define the order of execution

Qualify

Qualify register a qualifier for the component. Anyone can define a new qualifier.

type MyQualifier string

di.Register(func() *MyService {
	return &MyService{}
}, di.Qualify[testQualifier]())
Primary

Primary indicates that a component should be given preference when multiple candidates are qualified to inject a single-valued dependency. If exactly one 'primary' component exists among the candidates, it will be the injected value.

di.Register(func(repository FooRepository) FooService {
	return &FooServiceImpl{ repository: repository }
})

di.Register(func() FooRepository {
	return &MemoryRepositoryImpl{}
})

di.Register(func() FooRepository {
	return &DatabaseRepositoryImpl{}
}, di.Primary)

Because DatabaseRepositoryImpl is marked with Primary, it will be injected preferentially over the MemoryRepositoryImpl variant assuming both are present as component within the same di container.

Alternative

Alternative indicates that a component should NOT be given preference when multiple candidates are qualified to inject a single-valued dependency.

If exactly one NON-ALTERNATIVE component exists among the candidates, it will be the injected value.

di.Register(func(repository FooRepository) FooService {
	return &FooServiceImpl{ repository: repository }
})

di.Register(func() FooRepository {
	return &MemoryRepositoryImpl{}
})

di.Register(func() FooRepository {
	return &DatabaseRepositoryImpl{}
}, di.Alternative)

Because DatabaseRepositoryImpl is marked with Alternative, it will NOT be injected over the MemoryRepositoryImpl variant assuming both are present as component within the same di container.

Scoped

Scoped identifies the lifecycle of an instance, such as singleton, prototype, and so forth.. A scope governs how the container reuses instances of the type.

To register additional custom scopes, see Container.RegisterScope. Defaults to an empty string ("") which implies SCOPE_SINGLETON.

Singleton (aka Scoped("singleton"))

Singleton identifies a component that only instantiates once.

di.Register(func() MyService {
	return &MyServiceImpl{ Id: uuid.New() }
}, di.Singleton)

di.Register(func(s MyService) MyControllerA {
	print(s.Id) // uuid value
})

di.Register(func(s MyService) MyControllerB {
	print(s.Id) // same uuid value
})
Singleton (aka Scoped("prototype"))

Prototype identifies a component that a new instance is created every time the component factory is invoked.

di.Register(func() MyService {
	return &MyServiceImpl{ Id: uuid.New() }
}, di.Prototype)

di.Register(func(s MyService) MyControllerA {
	print(s.Id) // first uuid
})

di.Register(func(s MyService, ctn di.Container, ctx context.Context) MyControllerB {
	print(s.Id) // second uuid
	s2, _ := di.Get[testService](ctn, ctx)
	print(s2.Id) // third uuid
})
Condition

Condition a single condition that must be matched in order for a component to be registered.

Conditions are checked immediately before the component factory is due to be registered and are free to veto registration based on any criteria that can be determined at that point.

Stereotype

Stereotype a stereotype encapsulates any combination of ComponentOption

var Controller = di.Stereotype(di.Singleton, di.Qualify[testQualifier](), di.Startup(500))

di.Register(func() MyController {
    return &MyController{}
}, Controller)

Example: Filter using Stereotype

di.Filter(Controller).Foreach(func(f *Factory) (bool, error) { ... })

Qualify

Provider

Unmanaged

Scope

Utils

Instanciar um Componente ou Serviço na Inicializaçao

Componentes ou serviços podem ser inicialziados junto com o Container (di.Initialize(ctx)) por meio da configuração di.Startup(priority). Isso é útil para que você possa inicializar os serviços essenciais da sua aplicação de forma sincronizada, como por exemplo:

  • Executar scripts de migração de banco de dados
  • Fazer o carregamento das configurações do sistema
  • Registrar as suas controladores Rest ou seus endpoints
  • Executar validações do ambiente

Com o uso do di é possível implementar separadamente cada um desses serviços e especificar sua ordem de execução, conforme o exemplo abaixo.


// package config

di.Register(func() error {
	// we dont need to return a Component
    return doSomeEnvValidation()
}, di.Startup(0))

type JediService interface {
    FeelTheForce()
}

type PadawanController struct {
    s JediService
}

func (p * PadawanController) Initialize() {
    p.s.FeelTheForce()
}

// register as startup component, injecting dependencies
di.Register(func(s JediService) *PadawanController {
	return &PadawanController{s:s}
}, di.Startup(100))


// (...) in a package far, far away ...

type yodaServiceImp struct {}

func (s * yodaServiceImp) FeelTheForce() {
    print("Patience you must have my young Padawan")
}

di.Register(&yodaServiceImp{})

// package main

func main() {
    di.Initialize() 
}

Documentation

Overview

Exports the methods of a global instance to simplify the use of the lib

Index

Constants

View Source
const (
	SCOPE_SINGLETON string = "singleton"
	SCOPE_PROTOTYPE string = "prototype"
)

Variables

View Source
var (
	ErrCycleDetected         = errors.New("this component introduces a cycle")
	ErrManyCandidates        = errors.New("multiple candidates found")
	ErrContainerLocked       = errors.New("container is locked")
	ErrContextRequired       = errors.New("context required")
	ErrInvalidProvider       = errors.New("invalid provider")
	ErrMissingDependency     = errors.New("missing dependencies")
	ErrCandidateNotFound     = errors.New("no candidate found")
	ErrNoScopeNameDefined    = errors.New("no scope name defined for component")
	ErrCurrentlyInCreation   = errors.New("requested bean is currently in creation")
	ErrNoScopeNameRegistered = errors.New("no Scope registered")
)
View Source
var ErrNotStruct = errors.New("the Injected method only accepts struct or *struct")

Functions

func AllOf

func AllOf[T any](c Container, ctx context.Context) (o []T, e error)

func AllOfFilter

func AllOfFilter[T any](filter *FilteredFactories, ctx context.Context) (o []T, e error)

func Alternative

func Alternative(f *Factory)

Alternative indicates that a component should NOT be given preference when multiple candidates are qualified to inject a single-valued dependency.

If exactly one NON-ALTERNATIVE component exists among the candidates, it will be the injected value.

Example:

di.Register(func(repository FooRepository) FooService {
	return &FooServiceImpl{ repository: repository }
})

di.Register(func() FooRepository {
	return &MemoryRepositoryImpl{}
})

di.Register(func() FooRepository {
	return &DatabaseRepositoryImpl{}
}, di.Alternative)

Because DatabaseRepositoryImpl is marked with Alternative, it will NOT be injected over the MemoryRepositoryImpl variant assuming both are present as component within the same di container.

func Contains

func Contains(key reflect.Type) bool

Contains check if this container contains a component with the given key. Does not consider any hierarchy this container may participate in.

func ContainsRecursive

func ContainsRecursive(key reflect.Type) bool

func DefaultFactorySortLessFn

func DefaultFactorySortLessFn(a, b *Factory) bool

DefaultFactorySortLessFn is the default sorting function used by the Filter method, which sorts the factories in the following order:

1) Mock (test) 2) Primary 3) NOT Alternative 4) Lower Order

func Destroy

func Destroy() error

Destroy this container

func DestroyObject

func DestroyObject(key reflect.Type, object any) error

DestroyObject destroy the given instance

func DestroySingletons

func DestroySingletons() error

DestroySingletons destroy all singleton components in this container. To be called on shutdown of a factory.

func Get

func Get[T any](ctx ...context.Context) (o T, e error)

Get return an instance, which may be shared or independent, of the specified component.

func GetFrom

func GetFrom[T any](c Container, contexts ...context.Context) (o T, e error)

GetFrom get a instance from container using generics (returns error)

func Initialize

func Initialize(ctx ...context.Context) error

Initialize initialize all non-lazy singletons (startup)

func Injected

func Injected[T any]() func(Container, context.Context) (out T, err error)

Injected simplifies component registration through reflection.

Example:

type myController struct {
	MyService Service `inject:""`
}

di.Register(di.Injected[*myController]())

In the example above, the MyService dependency will be injected automatically.

func Key

func Key[T any]() reflect.Type

Key is a pointer to a type

func KeyOf

func KeyOf(t any) reflect.Type

KeyOf get a key for a value

func Mock

func Mock(mock any) (cleanup func())

Mock test only, register a mock instance to the container

func MustGetFrom

func MustGetFrom[T any](c Container, ctx ...context.Context) T

MustGetFrom get a instance from container using generics (panic on error)

func Primary

func Primary(f *Factory)

Primary indicates that a component should be given preference when multiple candidates are qualified to inject a single-valued dependency. If exactly one 'primary' component exists among the candidates, it will be the injected value.

Example:

di.Register(func(repository FooRepository) FooService {
	return &FooServiceImpl{ repository: repository }
})

di.Register(func() FooRepository {
	return &MemoryRepositoryImpl{}
})

di.Register(func() FooRepository {
	return &DatabaseRepositoryImpl{}
}, di.Primary)

Because DatabaseRepositoryImpl is marked with Primary, it will be injected preferentially over the MemoryRepositoryImpl variant assuming both are present as component within the same di container.

func Prototype

func Prototype(f *Factory)

Prototype identifies a component that a new instance is created every time the component factory is invoked.

Example:

di.Register(func() MyService {
	return &MyServiceImpl{ Id: uuid.New() }
}, di.Prototype)

di.Register(func(s MyService) MyControllerA {
	print(s.Id) // first uuid
})

di.Register(func(s MyService, ctn di.Container, ctx context.Context) MyControllerB {
	print(s.Id) // second uuid

	s2, _ := di.Get[testService](ctn, ctx)
	print(s2.Id) // third uuid
})

func Register

func Register(ctor any, opts ...FactoryConfig)

func RegisterScope

func RegisterScope(name string, scope ScopeI) error

RegisterScope Register the given scope, backed by the given ScopeI implementation.

func ResolveArgs

func ResolveArgs(factory *Factory, ctx ...context.Context) ([]reflect.Value, error)

ResolveArgs returns an ordered list of values which may be passed directly to the Factory Create method

func ShouldRegister

func ShouldRegister(ctor any, opts ...FactoryConfig) error

func Singleton

func Singleton(f *Factory)

Singleton identifies a component that only instantiates once.

Example:

di.Register(func() MyService {
	return &MyServiceImpl{ Id: uuid.New() }
}, di.Singleton)

di.Register(func(s MyService) MyControllerA {
	print(s.Id) // uuid value
})

di.Register(func(s MyService) MyControllerB {
	print(s.Id) // same uuid value
})

Types

type AlternativeQualifier

type AlternativeQualifier uint8

type Callback

type Callback func(any)

type ConditionFunc

type ConditionFunc func(Container, *Factory) bool

type Container

type Container interface {

	// Initialize initialize all non-lazy singletons (startup)
	Initialize(ctx ...context.Context) error

	Register(ctor any, opts ...FactoryConfig)

	ShouldRegister(ctor any, opts ...FactoryConfig) error

	// RegisterScope Register the given scope, backed by the given ScopeI implementation.
	RegisterScope(name string, scope ScopeI) error

	// Get return an instance, which may be shared or independent, of the specified component.
	Get(key reflect.Type, ctx ...context.Context) (any, error)

	// Contains check if this container contains a component with the given key.
	// Does not consider any hierarchy this container may participate in.
	Contains(key reflect.Type) bool

	ContainsRecursive(key reflect.Type) bool

	Filter(options ...FactoryConfig) *FilteredFactories

	GetObjectFactory(factory *Factory, managed bool, ctx ...context.Context) CreateObjectFunc

	GetObjectFactoryFor(key reflect.Type, managed bool, ctx ...context.Context) CreateObjectFunc

	// ResolveArgs returns an ordered list of values which may be passed directly to the Factory Create method
	ResolveArgs(factory *Factory, ctx ...context.Context) ([]reflect.Value, error)

	// Destroy this container
	Destroy() error

	// DestroyObject destroy the given instance
	DestroyObject(key reflect.Type, object any) error

	// DestroySingletons destroy all singleton components in this container. To be called on shutdown of a factory.
	DestroySingletons() error

	// Mock test only, register a mock instance to the container
	Mock(mock any) (cleanup func())
}

func Global

func Global() Container

func New

func New(parent Container) Container

type CreateObjectFunc

type CreateObjectFunc func() (any, DisposableAdapter, error)

func GetObjectFactory

func GetObjectFactory(factory *Factory, managed bool, ctx ...context.Context) CreateObjectFunc

func GetObjectFactoryFor

func GetObjectFactoryFor(key reflect.Type, managed bool, ctx ...context.Context) CreateObjectFunc

type Disposable

type Disposable interface {
	// Destroy invoked by the container on destruction of a component.
	Destroy()
}

Disposable interface to be implemented by components that want to release resources on destruction

See Disposer

type DisposableAdapter

type DisposableAdapter interface {
	Context() context.Context
	Dispose()
}

DisposableAdapter adapter that perform various destruction steps on a given component instance:

type Factory

type Factory struct {
	// contains filtered or unexported fields
}

Factory is a node in the dependency graph that represents a constructor provided by the user and the basic attributes of the returned component (if applicable)

func (*Factory) Alternative

func (f *Factory) Alternative() bool

Alternative returns true if this is a Alternative candidate for a component (has the qualifier AlternativeQualifier)

func (*Factory) Conditions

func (f *Factory) Conditions() []ConditionFunc

Conditions returns the list of conditions methods for this factory The component is only eligible for registration when all specified conditions match.

func (*Factory) Constructor

func (f *Factory) Constructor() reflect.Value

func (*Factory) Create

func (f *Factory) Create(args []reflect.Value) (any, error)

Create a new instance of component.

func (*Factory) Disposers

func (f *Factory) Disposers() []Callback

Disposers returns the list of disposer methods for this factory

func (*Factory) HasConditions

func (f *Factory) HasConditions() bool

HasConditions returns true if this factory has any condition method

func (*Factory) HasDisposers

func (f *Factory) HasDisposers() bool

HasDisposers returns true if this factory has any disposer method

func (*Factory) HasQualifier

func (f *Factory) HasQualifier(q reflect.Type) bool

HasQualifier return true if this Factory has the specified qualifier

func (*Factory) IsTrueSingleton

func (f *Factory) IsTrueSingleton() bool

IsTrueSingleton returns true if is a reference for a singleton instance

func (*Factory) Key

func (f *Factory) Key() reflect.Type

Key gets the factory component Key (Key = reflect.TypeOf(ComponentType))

func (*Factory) Mock

func (f *Factory) Mock() bool

Mock returns true if this is a Mock factory (testing)

func (*Factory) Order

func (f *Factory) Order() int

Order the order value of this factory

Higher values are interpreted as lower priority. As a consequence, the object with the lowest value has the highest priority.

Same order values will result in arbitrary sort positions for the affected objects.

func (*Factory) Params

func (f *Factory) Params() []reflect.Type

func (*Factory) Primary

func (f *Factory) Primary() bool

Primary returns true if this is a Primary candidate for a component (has the qualifier PrimaryQualifier)

func (*Factory) Prototype

func (f *Factory) Prototype() bool

Prototype returns true if the scope = 'prototype'

func (*Factory) Qualifiers

func (f *Factory) Qualifiers() []reflect.Type

Qualifiers returns the list of qualifiers for this factory

func (*Factory) ReturnErrorIdx

func (f *Factory) ReturnErrorIdx() int

func (*Factory) ReturnValueIdx

func (f *Factory) ReturnValueIdx() int

func (*Factory) ReturnsError

func (f *Factory) ReturnsError() bool

func (*Factory) ReturnsValue

func (f *Factory) ReturnsValue() bool

func (*Factory) Scope

func (f *Factory) Scope() string

Scope gets the scope name

func (*Factory) Singleton

func (f *Factory) Singleton() bool

Singleton returns true if the scope = 'singleton'

func (*Factory) Startup

func (f *Factory) Startup() bool

Startup returns true if this factory is configured to run during the container initialization

func (*Factory) Type

func (f *Factory) Type() reflect.Type

type FactoryConfig

type FactoryConfig func(*Factory)

FactoryConfig is the type to configure the component Factory. Container.Register accepts any number of config (this is functional option pattern).

func Condition

func Condition(condition ConditionFunc) FactoryConfig

Condition a single condition that must be matched in order for a component to be registered. Conditions are checked immediately before the component factory is due to be registered and are free to veto registration based on any criteria that can be determined at that point.

func Disposer

func Disposer[T any](disposer func(T)) FactoryConfig

Disposer register a disposal function to the component. A factory component may declare multiple disposer methods. If the factory returns nil, the disposer will be ignored

See Disposable

func Initializer

func Initializer[T any](initializer func(T)) FactoryConfig

Initializer register a initializer function to the component. A factory component may declare multiple initializers methods. If the factory returns nil, the initializer will be ignored

See Initializable

func Order

func Order(order int) FactoryConfig

Order can be applied to any component to indicate in what order they should be used.

Higher values are interpreted as lower priority. As a consequence, the object with the lowest value has the highest priority.

Same order values will result in arbitrary sort positions for the affected objects.

If the component is marked as Startup, the order determines its execution order.

Order is also used during dependency injection. The candidate with the lower order will be injected.

A framework can implement filters and use order to define the order of execution

func Qualify

func Qualify[Q any]() FactoryConfig

Qualify register a qualifier for the component. Anyone can define a new qualifier.

Example:

type MyQualifier string

di.Register(func() *MyService {
	return &MyService{}
}, di.Qualify[testQualifier]())

func Scoped

func Scoped(scope string) FactoryConfig

Scoped identifies the lifecycle of an instance, such as singleton, prototype, and so forth.. A scope governs how the container reuses instances of the type.

To register additional custom scopes, see Container.RegisterScope.

Defaults to an empty string ("") which implies SCOPE_SINGLETON.

func Startup

func Startup(order int) FactoryConfig

Startup indicates that this component must be initialized during container initialization (Container.Initialize method)

Example:

di.Register(func()  {
	print("Second")
}, Startup(200))

di.Register(func()  {
	print("First")
}, Startup(100))

func Stereotype

func Stereotype(options ...FactoryConfig) FactoryConfig

Stereotype a stereotype encapsulates any combination of ComponentOption

Example:

var Controller = di.Stereotype(di.Singleton, di.Qualify[testQualifier](), di.Startup(500))

di.Register(func() MyController {
	return &MyController{}
}, Controller)

Example: Filter using Stereotype

di.Filter(Controller).Foreach(func(f *Factory) (bool, error) { ... })

type FilteredFactories

type FilteredFactories struct {
	// contains filtered or unexported fields
}

func Filter

func Filter(options ...FactoryConfig) *FilteredFactories

func FilterOf

func FilterOf[T any](c Container) *FilteredFactories

func (*FilteredFactories) Foreach

func (f *FilteredFactories) Foreach(visitor func(f *Factory) (stop bool, err error)) error

func (*FilteredFactories) Instances

func (f *FilteredFactories) Instances(ctx context.Context) ([]any, error)

func (*FilteredFactories) Sort

func (f *FilteredFactories) Sort(less func(a, b *Factory) bool) *FilteredFactories

type Initializable

type Initializable interface {
	// Initialize invoked by the container on creation of a component.
	Initialize()
}

Initializable interface to be implemented by components that want to initialize resources on creation

See Initializer

type Parameter

type Parameter struct {
	// contains filtered or unexported fields
}

Parameter representação de um parametro usado na injeção de dependencias

func (*Parameter) Candidates

func (p *Parameter) Candidates() (list []*Factory)

Candidates list alternative matches (Ex. value = A, B implements A, B is candidate, if A is missing)

func (*Parameter) Factories

func (p *Parameter) Factories() (list []*Factory)

Factories list all candidates that exactly matches the type

func (*Parameter) HasCandidates

func (p *Parameter) HasCandidates() bool

HasCandidates checks if there is any candidate for this parameter

func (*Parameter) IsValidCandidate

func (p *Parameter) IsValidCandidate(f *Factory) (isCandidate bool, isExactMatch bool)

func (*Parameter) Key

func (p *Parameter) Key() reflect.Type

func (*Parameter) Provider

func (p *Parameter) Provider() bool

Provider indicates that this parameter is a provider (Ex. func(sq Provider[*testService])

func (*Parameter) Qualified

func (p *Parameter) Qualified() bool

Qualified indicates that this parameter is qualified (Ex. func(sq Qualified[*MyService, MyQualifier])

func (*Parameter) Qualifier

func (p *Parameter) Qualifier() reflect.Type

func (*Parameter) Unmanaged

func (p *Parameter) Unmanaged() bool

Unmanaged indicates that this parameter is a unmanaged provider (Ex. func(sq Unmanaged[*testService])

func (*Parameter) Value

func (p *Parameter) Value() reflect.Type

func (*Parameter) ValueOf

func (p *Parameter) ValueOf(value any) reflect.Value

type PrimaryQualifier

type PrimaryQualifier uint8

type Provider

type Provider[T any] struct {
	TypeBase[T]
	// contains filtered or unexported fields
}

Provider provides instances of T. For any type T that can be injected, you can also inject Provider<T>. Compared to injecting T directly, injecting Provider<T> enables:

  • lazy or optional retrieval of an instance.
  • breaking circular dependencies.

Example:

di.Register(func(sp di.Provider[testService]) {
	if condition {
		service, err := sp.Get()
	}
})

See Unmanaged

func (Provider[T]) Get

func (p Provider[T]) Get() (o T, e error)

Get the value

func (Provider[T]) With

func (p Provider[T]) With(supplier func() (any, error)) Provider[T]

type Qualified

type Qualified[T any, Q any] struct {
	TypeBase[T]
	// contains filtered or unexported fields
}

Qualified allows you to inject a dependency that has a qualifier

Example:

type MyQualifier string

di.Register(func(sq Qualified[*MyService, MyQualifier]) {
	myService := sq.Get()
})

func (Qualified[T, Q]) Get

func (q Qualified[T, Q]) Get() T

Get the value

func (Qualified[T, Q]) Qualifier

func (q Qualified[T, Q]) Qualifier() reflect.Type

Qualifier get the qualifier type

func (Qualified[T, Q]) With

func (q Qualified[T, Q]) With(value any) Qualified[T, Q]

WithValue create a new instance of Qualified with the value

type ScopeI

type ScopeI interface {
	// Get return the object with the given key from the underlying scope,
	// creating it if not found in the underlying storage mechanism.
	//
	// If CreateObjectFunc returns a disposer, the scope need to register a callback to
	// be executed on destruction of the specified object in the scope (or at
	// destruction of the entire scope, if the scope does not destroy individual
	//	objects but rather only terminates in its entirety).
	Get(context.Context, reflect.Type, *Factory, CreateObjectFunc) (any, error)

	// Remove the object with the given key from the underlying scope.
	// Returns nil if no object was found; otherwise returns the removed Object.
	Remove(reflect.Type, any) (any, error)

	Destroy()
}

type TypeBase

type TypeBase[T any] struct {
}

func (TypeBase[T]) Type

func (t TypeBase[T]) Type() reflect.Type

Type get the type of component

type Unmanaged

type Unmanaged[T any] struct {
	TypeBase[T]
	// contains filtered or unexported fields
}

Unmanaged Provider allow the creation of unmanaged instances. The container will not keep track of these instances and the application will be responsible for cleaning them up.

Example:

di.Register(func(up di.Unmanaged[*Seat]) {
	driver, err := sp.Get()
	passenger, err := sp.Get()
})

See Provider

func (Unmanaged[T]) Get

func (u Unmanaged[T]) Get() (o T, a DisposableAdapter, e error)

Get the value

func (Unmanaged[T]) Unmanaged

func (u Unmanaged[T]) Unmanaged() bool

func (Unmanaged[T]) With

func (u Unmanaged[T]) With(supplier func() (any, DisposableAdapter, error)) Unmanaged[T]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL