Documentation
¶
Index ¶
- Variables
- func ClosedWithScope() containercontract.OverrideOption
- func FromResolver[T any](resolver containercontract.Resolver, serviceName string) (T, error)
- func FromResolverByType[T any](resolver containercontract.Resolver) (T, error)
- func MustFromResolver[T any](resolver containercontract.Resolver, serviceName string) T
- func MustFromResolverByType[T any](resolver containercontract.Resolver) T
- func MustRegister[T any](registrar containercontract.Registrar, serviceName string, ...)
- func MustRegisterScoped[T any](registrar containercontract.ScopedRegistrar, serviceName string, ...)
- func MustRegisterScopedType[T any](registrar containercontract.ScopedRegistrar, ...)
- func MustRegisterType[T any](registrar containercontract.Registrar, provider containercontract.Provider[T], ...)
- func NewContainer() containercontract.Container
- func Register[T any](registrar containercontract.Registrar, serviceName string, ...) error
- func RegisterScoped[T any](registrar containercontract.ScopedRegistrar, serviceName string, ...) error
- func RegisterScopedType[T any](registrar containercontract.ScopedRegistrar, ...) error
- func RegisterType[T any](registrar containercontract.Registrar, provider containercontract.Provider[T], ...) error
- func Replacing() containercontract.RegisterOption
- func WithTypeRegistration(isStrict bool) containercontract.RegisterOption
- func WithoutTypeRegistration() containercontract.RegisterOption
- type LazyService
Constants ¶
This section is empty.
Variables ¶
var ErrScopedServiceIdAlreadyRegistered = errors.New("scoped service already registered")
ErrScopedServiceIdAlreadyRegistered is the cause of a RegisterScoped refusal for a duplicate scoped service name. It is distinct from the container's duplicate so a boot collision report can say which lifetime the name was already taken at.
var ErrScopedServiceTypeAlreadyRegistered = errors.New("scoped service type already registered")
ErrScopedServiceTypeAlreadyRegistered is the cause of a strict scoped type-registration refusal for a duplicate service type.
var ErrServiceIdAlreadyRegistered = errors.New("service already registered")
ErrServiceIdAlreadyRegistered is the cause of a Register refusal for a duplicate service name; classify with errors.Is so callers (the application boot collision report) can tell a duplicate apart from other registration failures.
var ErrServiceTypeAlreadyRegistered = errors.New("service type already registered")
ErrServiceTypeAlreadyRegistered is the cause of a strict type-registration refusal for a duplicate service type.
Functions ¶
func ClosedWithScope ¶ added in v2.13.0
func ClosedWithScope() containercontract.OverrideOption
ClosedWithScope hands the installed value to the scope's teardown, alongside the services the scope built for itself. The default is the opposite: an override is installed from outside and belongs to whoever installed it, because the installer usually still needs it after the scope is gone. This option is for the caller that built the value for this one scope and holds no other reference to it.
func FromResolver ¶
func FromResolver[T any](resolver containercontract.Resolver, serviceName string) (T, error)
func FromResolverByType ¶
func FromResolverByType[T any](resolver containercontract.Resolver) (T, error)
func MustFromResolver ¶
func MustFromResolver[T any](resolver containercontract.Resolver, serviceName string) T
func MustFromResolverByType ¶
func MustFromResolverByType[T any](resolver containercontract.Resolver) T
func MustRegister ¶
func MustRegister[T any]( registrar containercontract.Registrar, serviceName string, provider containercontract.Provider[T], options ...containercontract.RegisterOption, )
func MustRegisterScoped ¶ added in v2.13.0
func MustRegisterScoped[T any]( registrar containercontract.ScopedRegistrar, serviceName string, provider containercontract.Provider[T], options ...containercontract.RegisterOption, )
func MustRegisterScopedType ¶ added in v2.13.0
func MustRegisterScopedType[T any]( registrar containercontract.ScopedRegistrar, provider containercontract.Provider[T], options ...containercontract.RegisterOption, )
func MustRegisterType ¶
func MustRegisterType[T any]( registrar containercontract.Registrar, provider containercontract.Provider[T], options ...containercontract.RegisterOption, )
func NewContainer ¶
func NewContainer() containercontract.Container
func Register ¶
func Register[T any]( registrar containercontract.Registrar, serviceName string, provider containercontract.Provider[T], options ...containercontract.RegisterOption, ) error
func RegisterScoped ¶ added in v2.13.0
func RegisterScoped[T any]( registrar containercontract.ScopedRegistrar, serviceName string, provider containercontract.Provider[T], options ...containercontract.RegisterOption, ) error
RegisterScoped is Register's counterpart for a service whose lifetime is one scope. It takes a ScopedRegistrar and nothing else: the two registrar interfaces share no method, so handing this function the container registrar a RegisterServices hook was given — or handing Register the scoped registrar a RegisterScopedServices hook was given — does not compile. That is the whole guard, and it is why the lifetime is spelled in the verb rather than left to a comment.
func RegisterScopedType ¶ added in v2.13.0
func RegisterScopedType[T any]( registrar containercontract.ScopedRegistrar, provider containercontract.Provider[T], options ...containercontract.RegisterOption, ) error
func RegisterType ¶
func RegisterType[T any]( registrar containercontract.Registrar, provider containercontract.Provider[T], options ...containercontract.RegisterOption, ) error
func Replacing ¶ added in v2.13.0
func Replacing() containercontract.RegisterOption
Replacing admits a SCOPED registration whose name — or whose registered type — the container already holds. Without it the collision is refused where it is made, because a name that means two things depending on where it is asked from is the ambiguity the two lifetimes exist to keep apart. It is read only by the scoped registration paths: a container registration declaring it gains nothing, and takes a scoped-owned name only when the scoped side itself was declared Replacing.
Declaring it where nothing collides is not inert: the waiver is remembered, and a container registration of the same name arriving later is admitted without a declaration of its own. The waiver likewise covers the name and the registered type together — a scoped registration admitted for its name also shadows, inside every scope, the type-keyed resolution of whichever container service shares the type; a registration that means only the name opts out of the type with WithoutTypeRegistration.
func WithTypeRegistration ¶
func WithTypeRegistration(isStrict bool) containercontract.RegisterOption
func WithoutTypeRegistration ¶
func WithoutTypeRegistration() containercontract.RegisterOption
Types ¶
type LazyService ¶ added in v2.11.0
type LazyService[T any] struct { // contains filtered or unexported fields }
LazyService defers resolving a container service until its first use and memoizes success only — a failed or nil resolution is retried on the next call, mirroring the container's own resolver. A component assembled during the boot phase — a cli command, an http middleware — can hold a service whose provider is registered but not yet safe to resolve at that phase, without hand-rolling a deferred-resolution proxy for each one. A genuinely app-specific proxy over the app's own interface is still built on this handle.
A handle built over the resolver a provider was handed records the dependency when it finally resolves, so the teardown closes the service holding the handle before the service the handle produced — the ordering is the same one construction-time resolution gets, and it holds however late the first use is. A handle built over the CONTAINER itself has no such owner: nothing says which service it belongs to, so what it resolves is ordered against the holder by name like any unrelated pair, and a holder that drains through a handle at Close may find it already ended. Build the handle over the provider's resolver where the ordering matters.
A handle follows the scope of the resolver it was built over: the memoized value is served for as long as that scope lives, and once the scope reports itself closed the handle answers the scope-is-closed error and drops the value, the closure and the resolver, keeping no path to the dead request's state. The resolver is captured at construction, so one handle never answers for two scopes: code shared across requests resolves per call through FromResolver with the current request's resolver — the value is then keyed to the right scope by the scope's own instance map.
func Lazy ¶ added in v2.11.0
func Lazy[T any](resolver containercontract.Resolver, serviceName string) *LazyService[T]
Lazy returns a handle that resolves serviceName from the resolver on first use, the deferred form of FromResolver / MustFromResolver.
func LazyByType ¶ added in v2.11.0
func LazyByType[T any](resolver containercontract.Resolver) *LazyService[T]
LazyByType returns a handle that resolves the service by its type on first use, the deferred form of FromResolverByType / MustFromResolverByType.
func (*LazyService[T]) Get ¶ added in v2.11.0
func (instance *LazyService[T]) Get() T
Get resolves the service and returns the memoized value once a resolution has succeeded, panicking if the resolution fails or yields nil — the deferred equivalent of MustFromResolver; because neither a failure nor a nil yield is memoized, the next call retries the resolution.
func (*LazyService[T]) Resolve ¶ added in v2.11.0
func (instance *LazyService[T]) Resolve() (T, error)
Resolve resolves the service and memoizes success only: a successfully resolved non-nil value is returned on every later call without re-running the resolver, while a failed resolution returns the error without memoizing it and a nil yield is likewise passed through unmemoized, so the next call retries either — a transient outage at first use does not poison the handle; use Get for the panic-on-failure path. The memoization holds only while the resolver's scope lives: a resolver that can answer the liveness question and reports itself closed turns the handle terminal — the scope-is-closed error on this and every later call, with the value, the closure and the resolver dropped — because the memoized value is that dead request's state and the alternative was serving it to every later caller forever. A resolver that cannot answer the question is read as open, exactly as the exit handler reads a logger that cannot. The resolver runs outside the handle's lock, so a resolver that reaches back into this same handle does not deadlock against the handle's own synchronization: given a handle built over a live resolver context, the re-entry reaches the container's cycle detection and surfaces as a circular-dependency error. A handle built over the container itself (the resolver argument being the container rather than a provider's resolver context) is a different matter — every container.Get mints a fresh resolution context, so a re-entrant chain through such a handle blocks on the container's own creation wait rather than being reported as a cycle; that is a property of the container's resolution, not of this handle, and it is unchanged by the lock scope. When several first uses race, each may run the resolver and the first to store wins; the container's own memoization makes the duplicates converge for shared services.
Source Files
¶
- container.go
- container_close.go
- container_register.go
- container_register_scoped.go
- container_registrar.go
- container_resolver.go
- container_scoped_registrar.go
- errors.go
- lazy.go
- override_option.go
- register_option.go
- registrar_provider.go
- resolver.go
- resolver_context.go
- resolver_context_scoped.go
- scope.go
- scope_registrar.go
- utility.go