genjector

package module
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2022 License: MIT Imports: 1 Imported by: 0

README

Go Reference

Package Genjector

Reflection-free Run-Time Dependency Injection framework for Go 1.18+

Read blog for better examples!

The Goal

The purpose of The Genjector package is to provide a Dependency Injection framework without relying on reflection and depending solely on Go Generics (provided from Go version 1.18).

It supports many different features:

  • Binding concrete implementations to particular interfaces.
  • Binding implementations as pointers or values.
  • Binding implementations with Provider methods.
  • Binding implementations with concrete instances.
  • Define Binding as singletons.
  • Define annotations for Binding.
  • Define slices and maps of implementations.
  • ...

Benchmark

While providing the most of known features of Dependency Injection frameworks, The Genjector Package also delivers top performance comparing to current widely used Run-Time DI frameworks, (so, solutions based on code generators are excluded).

goos: darwin
goarch: amd64
pkg: github.com/ompluscator/genjector/_benchmark
cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz
Benchmark
Benchmark/github.com/golobby/container/v3
Benchmark/github.com/golobby/container/v3-8         	 2834061	       409.6 ns/op
Benchmark/github.com/goava/di
Benchmark/github.com/goava/di-8                     	 4568984	       261.9 ns/op
Benchmark/github.com/goioc/di
Benchmark/github.com/goioc/di-8                     	19844284	        60.66 ns/op
Benchmark/go.uber.org/dig
Benchmark/go.uber.org/dig-8                         	  755488	      1497 ns/op
Benchmark/flamingo.me/dingo
Benchmark/flamingo.me/dingo-8                       	 2373394	       503.7 ns/op
Benchmark/github.com/samber/do
Benchmark/github.com/samber/do-8                    	 3585386	       336.0 ns/op
Benchmark/github.com/ompluscator/genjector
Benchmark/github.com/ompluscator/genjector-8        	21460600	        55.71 ns/op
Benchmark/github.com/vardius/gocontainer
Benchmark/github.com/vardius/gocontainer-8          	60947049	        20.25 ns/op
Benchmark/github.com/go-kata/kinit
Benchmark/github.com/go-kata/kinit-8                	  733842	      1451 ns/op
Benchmark/github.com/Fs02/wire
Benchmark/github.com/Fs02/wire-8                    	25099182	        47.43 ns/op
PASS

Examples

Detailed examples can be found inside inner "examples" package.

Some simple code blocks can be found bellow.

Simple Pointer
package example

type ServiceInterface interface {
  String() string
}

type Service struct {
  value string
}

func (s *Service) Init() {
  s.value = "value provided inside the Service"
}

func (s *Service) String() string {
  return s.value
}

err := genjector.Bind(genjector.AsPointer[ServiceInterface, *Service]())
if err != nil {
  return err
}

instance, err := genjector.NewInstance[ServiceInterface]()
if err != nil {
  return err
}

value := instance.String()
if value != "value provided inside the Service" {
  return err
}
Complex Pointer
package example

type ServiceInterface interface {
  String() string
}

type Service struct {
  value string
}

func (s *Service) Init() {
  s.value = "value provided inside the Service"
}

func (s *Service) String() string {
  return s.value
}

err := genjector.Bind(
  genjector.AsPointer[ServiceInterface, *Service](),
  genjector.AsSingleton(),
  genjector.WithAnnotation("service")
)
if err != nil {
  return err
}

instance, err := genjector.NewInstance[ServiceInterface](
  genjector.WithAnnotation("service"),
)
if err != nil {
  return err
}

value := instance.String()
if value != "value provided inside the Service" {
  return err
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bind

func Bind[T any](source BindingSource[T], options ...BindingOption) error

Bind executes complete logic for binding particular value (or pointer) to desired interface (or struct). By default, it stores all Binding instances into default inner Container.

It requires only BindingSource to be passed as an argument and all other instances of BindingOption are optional.

At this point, if Binding for particular interface (or struct) is not defined, it uses its own fallback Binding. Still, it works fully only for values, not pointers, as for pointers it returns nil value. That means that pointer Binding should be always defined.

func Clean

func Clean()

Clean creates a new instance of inner Container.

func MustBind

func MustBind[T any](source BindingSource[T], options ...BindingOption)

MustBind wraps Bind method, by making sure error is not returned as an argument.

Still, in case of error, it panics.

func MustNewInstance added in v1.1.0

func MustNewInstance[T any](options ...KeyOption) T

MustNewInstance wraps NewInstance method, by making sure error is not returned as an argument.

Still, in case of error, it panics.

func NewInstance added in v1.1.0

func NewInstance[T any](options ...KeyOption) (T, error)

NewInstance executes complete logic for initializing value (or pointer) for desired interface (or struct). By default, it uses Binding instance from default inner Container. If such Binding can not be found, it tries to make its own fallback Binding.

All instances of BindingOption are optional.

At this point, if Binding for particular interface (or struct) is not defined, it uses its own fallback Binding. Still, it works fully only for values, not pointers, as for pointers it returns nil value. That means that pointer Binding should be always defined.

Types

type Binding

type Binding interface {
	Instance(initialize bool) (interface{}, error)
}

Binding represents an interface that delivers new instance for particular interface (or a struct).

type BindingOption

type BindingOption interface {
	Key(key Key) Key
	Container(container Container) Container
	Binding(binding Binding) (Binding, error)
}

BindingOption represents an interface that overrides creation of Key, Binding and Container.

func AsSingleton

func AsSingleton() BindingOption

AsSingleton delivers a BindingOption that defines the instance of desired Binding as a singleton. That means only first time the Init method (or ProviderMethod) will be called, and every next time the same instance will be delivered as a result of NewInstance method.

Example: err := genjector.Bind(

genjector.AsPointer[SingletonInterface, *SingletonStruct](),
genjector.AsSingleton(),

)

AsSingleton should be only used as a BindingOption for Bind method, as it does not affect functionality if it is used in NewInstance method.

func WithAnnotation

func WithAnnotation(annotation string) BindingOption

WithAnnotation delivers a BindingOption that allows to name specific Binding with any annotation desired.

Example: err = genjector.Bind(

genjector.AsPointer[AnnotationInterface, *AnnotationStruct](),
genjector.WithAnnotation("first"),

)

To properly use a customer Container, WithAnnotation should be used in both Bind and NewInstance methods.

func WithContainer

func WithContainer(container Container) BindingOption

WithContainer delivers a BindingOption that overrides the usage of standard internal (global) Container. It allows to provide a fresh, a custom instance of Container, that can be made from NewContainer method.

Example: err := genjector.Bind(

genjector.AsPointer[ContainerInterface, *ContainerStruct](),
genjector.WithContainer(customContainer),

)

To properly use a customer Container, WithContainer should be used in both Bind and NewInstance methods.

type BindingSource

type BindingSource[T any] interface {
	Key() Key
	Binding() (Binding, error)
}

BindingSource represents an interface that delivers starting Key and Binding instances, that later could be overridden by KeyOption or BindingOption.

func AsInstance

func AsInstance[T any, S any](instance S) BindingSource[T]

AsInstance delivers a BindingSource for a type T, by using a concrete instance that is passed as an argument to AsInstance method, to returns that instance whenever it is required from Binding.

Example:

err := genjector.Bind(genjector.AsInstance[*InstanceStruct](&InstanceStruct{
  value: "value provided in concrete instance",
}))

BindingSource can be only used as the first argument to Bind method.

func AsPointer added in v1.1.0

func AsPointer[T any, S *R, R any]() BindingSource[T]

AsPointer delivers a BindingSource for a type T, by binding pointer of a struct to the concrete interface (or the struct itself). It must be only used with pointers and not values. In case values is used, code will panic.

Example: err := genjector.Bind(genjector.AsPointer[PointerInterface, *PointerStruct]())

BindingSource can be only used as the first argument to Bind method.

func AsProvider

func AsProvider[T any, S any](provider ProviderMethod[S]) BindingSource[T]

AsProvider delivers a BindingSource for a type T, by defining a ProviderMethod (or constructor method) for the new instance of some interface (or a struct).

Example:

err := genjector.Bind(genjector.AsProvider[ProviderInterface](func() (*ProviderStruct, error) {
  return &ProviderStruct{
    value: "value provided inside the ProviderMethod",
  }, nil
}))

BindingSource can be only used as the first argument to Bind method.

func AsValue

func AsValue[T any, S any]() BindingSource[T]

AsValue delivers a BindingSource for a type T, by binding a value of a struct to the concrete interface (or the struct itself). It must be only used with value and not pointer. In case pointer is used, code will return a nil value for the instance.

Example: err := genjector.Bind(genjector.AsValue[ValueInterface, ValueStruct]())

BindingSource can be only used as the first argument to Bind method.

func InMap

func InMap[K comparable, T any](key K, source BindingSource[T]) BindingSource[T]

InMap delivers a BindingSource for a type T and key's type K, that creates a map of K-T pairs. It is used as a wrapping BindingSource for any other inner. It creates complex Binding in the background that stores all T types in a map and delivers it upon request by executing NewInstance method for a K-T map.

Example: err := :genjector.Bind(

  genjector.InMap(
    "third",
    genjector.AsInstance[MapInterface](&MapStruct{
      value: "concrete value",
	   }),
  ),

)

BindingSource can be only used as the first argument to Bind method.

func InSlice

func InSlice[T any](source BindingSource[T]) BindingSource[T]

InSlice delivers a BindingSource for a slice of types T.. It is used as a wrapping BindingSource for any other inner. It creates complex Binding in the background that stores all T types in a slice and delivers it upon request by executing NewInstance method for a slice of T types.

Example: err := :genjector.Bind(

  genjector.InSlice(
    genjector.AsInstance[SliceInterface](&SliceStruct{
      value: "concrete value",
	   }),
  ),

)

BindingSource can be only used as the first argument to Bind method.

type Container

type Container map[interface{}]Binding

Container is a child type used for storing all Binding instances.

func NewContainer

func NewContainer() Container

NewContainer delivers a new instance of Container.

type FollowingBindingSource

type FollowingBindingSource[T any] interface {
	SetPrevious(binding Binding)
}

FollowingBindingSource represents an interface for a Binding instance that requires to get previous instance of Binding inside Container, before the new one should be stored on that place.

type Initializable

type Initializable interface {
	Init()
}

Initializable represents any struct that contains a method Init. When such struct as defined AsPointer or AsValue, method Init will be called during initialization process.

type Key

type Key struct {
	Annotation string
	Value      interface{}
}

Key is a struct that contains information for Binding keys inside a Container.

It is meant to be used only for internal purposes.

func (Key) Generate

func (k Key) Generate() interface{}

Generate delivers a final Binding key for the Container.

type KeyOption

type KeyOption interface {
	Key(key Key) Key
	Container(container Container) Container
}

KeyOption represents an interface that overrides creation of Key and Container.

type KeySource

type KeySource interface {
	Key() Key
}

KeySource represents an interface that builds a Key for Binding.

type ProviderMethod

type ProviderMethod[S any] func() (S, error)

ProviderMethod defines a type of a method that should delivers an instance od type S. This method acts as an constructor method and it is executed at the time of NewInstance method.

It respects Binding interface.

func (ProviderMethod[S]) Instance

func (s ProviderMethod[S]) Instance(bool) (interface{}, error)

Instance delivers the concrete instance of type S, by executing root ProviderMethod itself.

It respects Binding interface.

Jump to

Keyboard shortcuts

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