defval / di Goto Github PK
View Code? Open in Web Editor NEW๐ A full-featured dependency injection container for go programming language.
License: MIT License
๐ A full-featured dependency injection container for go programming language.
License: MIT License
Hey,
Maybe a bit related to #39 but on the Provide side of things.
It seems that I cannot do this, why? Seems like a better way than using tags to identify strings.
package main
import (
"github.com/defval/di"
)
type ProjectName string
func main() {
_, err := di.New(
di.ProvideValue("project1", di.As(new(ProjectName))),
)
if err != nil {
panic(err)
}
}
I'm getting *main.ProjectName: not a pointer to interface
Tracing is very simple. Need to show more information about interface and group resolving.
go get github.com/defval/di
go: github.com/defval/[email protected]: parsing go.mod:
module declares its path as: github.com/goava/di
but was required as: github.com/defval/di
go get github.com/goava/di
works
Is it possible to override a value that is already defined in a parent container?
For example, let's say I want to override the Logger for a specific DI container like this
type Logger interface {
}
type Logger1 struct{}
type Logger2 struct{}
func main() {
root, err := di.New(
di.ProvideValue(Logger1{}, di.As(new(Logger))),
)
if err != nil {
panic(err)
}
child, err := di.New(
di.ProvideValue(Logger2{}, di.As(new(Logger))),
)
if err != nil {
panic(err)
}
err = child.AddParent(root)
if err != nil {
panic(err)
}
var lg Logger
err = child.Resolve(&lg)
if err != nil {
panic(err)
}
}
I get
multiple definitions of main.Logger, maybe you need to use group type: []main.Logger
I started to try to implement this but I dont really understand how the internals are working right now. My Idea was to add another Option to di which is called Decorate
.
package main
import (
"fmt"
"github.com/goava/di"
)
func main() {
container, err := di.New(
di.Provide(NewExample),
)
if err != nil {
panic(err)
}
container.Invoke(func(e Example) {
fmt.Println(e) // Will print "Example"
})
err = container.Decorate(DecorateExample)
if err != nil {
panic(err)
}
container.Invoke(func(e Example) {
fmt.Println(e) // Will print "Example is now Decorated"
})
}
type Example string
func NewExample() Example {
return "Example"
}
func DecorateExample(e Example) Example {
return e + " is now Decorated"
}
This can also be expaned to have options like priority to allow ordered decorations. Of course this only works when the decorated type isnt used before decoration. So you would have to call Decorate before Invoking anything.
When I provide the Container with multiple implementations of an Object and have one that doesnt have a Name the container doesnt know how to resolve it properly and returns an error when resolving a named entry
This is counter intuitive and should either be explained in the docs or removed
Hello.
I'm considering introducing DI into my project. Your DI implementation container looks good enough :) But I have a question: how to create services that are created through user's configuration? Here's an example:
package main
import (
"github.com/goava/di"
"github.com/spf13/viper"
)
type ServiceInterface interface {
Do()
}
type DependencyA interface {}
type ServiceImplementationA struct {DependencyA}
func (s *ServiceImplementationA) Do() {}
type DependencyB interface {}
type ServiceImplementationB struct {DependencyB}
func (s *ServiceImplementationB) Do() {}
func NewService(config *viper.Viper) ServiceInterface {
if viper.GetString("preferred_service") == "B" {
// Resolve as ServiceImplementationB
}
// Otherwise resolve as ServiceImplementationA
}
func main() {
container, _ := di.New(
di.Provide(viper.New),
di.Provide(NewService),
)
var service ServiceInterface
container.Resolve(&service)
}
Depending on the configuration, I want to create a service interface implementation. But each implementation has its own dependencies list (I have not provided them manually in the example). I need a way to somehow call container.Resolve(&concreteImplementation)
from the NewService
function.
Is there any way to do this? Or maybe I just misunderstand how it should be done at all?
Hello,
I hope this message finds you well. I am writing to inform you that I'm planning to transfer this Go library to my personal GitHub account this upcoming Monday, June 5th. This is an important change, and I want to explain how it could impact you, and how to mitigate any issues.
The good news is that GitHub will automatically redirect all requests from the old repository to the new one. This means that your current imports should continue to work as they are. However, the import paths in the new repository will change to reflect its new location, and I highly recommend updating your Go code to use these new import paths once the transfer is complete.
Here's what you can do to prepare:
github.com/defval/di
I understand that this change might cause some inconvenience, and I apologize for that.
I greatly appreciate your understanding ๐ค
Currently injected structs resolve using tag constraint like so:
type Parameters struct {
di.Inject
// use tag for the container to know that field need to be injected.
Leader *DatabaseConfig `type:"leader"`
Follower *DatabaseConfig `type:"follower"`
}
But things start to break down if that same struct is used in other ways, for example marshalling:
type Parameters struct {
di.Inject
// use tag for the container to know that field need to be injected.
Leader *DatabaseConfig `type:"leader" json:"leader"`
Follower *DatabaseConfig `type:"follower" json:"follower"`
}
To avoid conflicting with other libs that also use tags to provide features on structs, I propose that we change how goava/di looks for struct tags that look like this instead:
type Parameters struct {
di.Inject
// use tag for the container to know that field need to be injected.
Leader *DatabaseConfig `di:"type=leader"`
Follower *DatabaseConfig `di:"type=follower"`
}
I am currently using a patched version of uber-go/fx and uber-go/dig where I already added these Features. It allows to execute code and add more options based on things that are in the Container.
type Configuration struct {
Enabled bool
}
di.New(
di.Provide(func () *Configuration {
return &Configuration{ true }
}),
di.Provide(func(configuration *Configuration) di.Option {
if configuration.Enabled {
return di.Invoke(func () { fmt.Println("Hello World") })
}
return nil
})
)
Actually, the title. I've prepared an example that shows the problem:
https://play.golang.org/p/vZ2JvfmvJBB
package main
import (
"log"
"github.com/goava/di"
)
type Dependency struct {}
func NewDependency() *Dependency {
return &Dependency{}
}
type Params struct {
di.Inject
Dependency *Dependency `di:""`
}
func InvokeWithDependencyAsParam(params Params) {
// Some code
}
func main() {
_, err := di.New(
di.Provide(NewDependency),
di.Invoke(InvokeWithDependencyAsParam),
)
if err != nil {
log.Fatal(err)
}
}
The title :)
https://play.golang.org/p/ZvWhZaxyzM6
package main
import (
"context"
"log"
"github.com/goava/di"
)
type Interface interface {
Do(ctx context.Context) error
}
type FuncInterface func(ctx context.Context) error
func (c FuncInterface) Do(ctx context.Context) error {
return c(ctx)
}
func createFirst() FuncInterface {
return func(ctx context.Context) error {
return nil
}
}
func createSecond() FuncInterface {
return func(ctx context.Context) error {
return nil
}
}
func main() {
container, err := di.New(
di.Provide(createFirst, di.As(new(Interface))),
di.Provide(createSecond, di.As(new(Interface))),
)
if err != nil {
log.Fatal(err)
}
var interfaces []Interface
err = container.Resolve(&interfaces)
if err != nil {
log.Fatal(err)
}
}
The error:
main.FuncInterface already exists in dependency graph
It would be nice if you could auto resolve from another container. Example:
type Message string
parent, _ := di.New(di.ProvideValue(Message("Hello from parent")))
child1, _ := di.New(di.Chain(parent))
child1.Invoke(func(m Message) { fmt.Println(m) })
This would let us have common injection providers in a parent, but still have some child Containers that can inject some variations. Some DI systems call these DI scopes I think.
The title :)
Test case (container_test:840):
t.Run("constructor with injectable embed pointer", func(t *testing.T) {
c, err := di.New()
require.NoError(t, err)
type InjectableType struct {
di.Inject
*http.ServeMux
}
mux := &http.ServeMux{}
require.NoError(t, c.Provide(func() *http.ServeMux { return mux }))
require.NoError(t, c.Provide(func() *InjectableType { return &InjectableType{} }))
var result *InjectableType
require.NoError(t, c.Resolve(&result))
require.NotNil(t, result.ServeMux)
require.Equal(t, fmt.Sprintf("%p", mux), fmt.Sprintf("%p", result.ServeMux))
})
Hi,
Is there a concept of a singleton? For example, database connections?
Thank you
I am currently using uber-go/fx and I want to switch to DI, but sadly DI is missing a core feature to not run the invokes in compile state, instead having a Start() and Stop() function to kick off the Invokes and run until a control signal got sent, like CTRL+C.
container := di.New(
// options
)
startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := container.Start(startCtx); err != nil {
log.Fatal(err)
}
<- container.Done()
stopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := container.Stop(stopCtx); err != nil {
log.Fatal(err)
}
or less verbose if someone doesnt need full controll over the start and stop system
container := di.New(
// options
)
container.Run()
my project full of injection functions like this
type Controller struct {
user types.UserService
cfg *config.Config
friend types.FriendService
}
func NewController(cfg *config.Config, user types.UserService, friend types.FriendService) *Controller {
return &Controller{
user: user,
cfg: cfg,
friend: friend,
}
}
I hope to support tag
auto inject E.g: di.Parameter
type Controller struct {
User types.UserService `di:""` // has `di` tag auto inject, di tag with name
Cfg *config.Config `di:""`
Friend types.FriendService `di:""`
}
func NewController() *Controller {
return &Controller{}
}
Here is my simple implementation
diff --git a/parameter.go b/parameter.go
index acfe8e0..58a504b 100644
--- a/parameter.go
+++ b/parameter.go
@@ -2,6 +2,8 @@ package di
import (
"reflect"
+ "strings"
+ "unsafe"
)
// isEmbedParameter
@@ -61,9 +63,63 @@ func (p parameter) ResolveValue(c *Container) (reflect.Value, error) {
if cleanup != nil {
c.cleanups = append(c.cleanups, cleanup)
}
+ // inject struct
+ err = p.ResolveProperty(c, value)
+ if err != nil {
+ return reflect.Value{}, err
+ }
return value, nil
}
+const (
+ flagRO = 0b1100000
+)
+
+func ValuePatch(v reflect.Value) reflect.Value {
+ rv := reflect.ValueOf(&v)
+ flag := rv.Elem().FieldByName("flag")
+ ptrFlag := (*uintptr)(unsafe.Pointer(flag.UnsafeAddr()))
+ *ptrFlag = *ptrFlag &^ flagRO
+ return v
+}
+
+func (p parameter) ResolveProperty(c *Container, value reflect.Value) (err error) {
+ value = reflect.Indirect(value)
+ if value.Kind() != reflect.Struct {
+ return nil
+ }
+ vType := value.Type()
+ for i := 0; i < value.NumField(); i++ {
+ fieldType := vType.Field(i)
+ field := ValuePatch(value.Field(i))
+ tag, ok := fieldType.Tag.Lookup("di")
+ if ok {
+ var optional = false
+ var name string
+ tags := strings.Split(tag, ",")
+ for _, t := range tags {
+ t = strings.Trim(t, " ")
+ if t == "optional" {
+ optional = true
+ continue
+ }
+ name = t
+ }
+ pp := parameter{
+ name: name,
+ typ: fieldType.Type,
+ optional: optional,
+ }
+ param, err := pp.ResolveValue(c)
+ if err != nil {
+ return err
+ }
+ field.Set(param)
+ }
+ }
+ return nil
+}
+
// internalParameter
type internalParameter interface {
isDependencyInjectionParameter()
ValuePatch
is private field set patch func, not necessary
Hello
Here the the test case :
package main
import (
"fmt"
"github.com/goava/di"
)
type I interface{}
type A struct {}
type B struct {}
func main() {
c, err := di.New()
if err != nil {
panic(err)
}
c.Provide(func() I {
return A{}
}, di.As(new(I)))
c.Provide(func() I {
return B{}
}, di.As(new(I)))
is := make([]I, 0)
err = c.Resolve(&is)
if err != nil {
panic(err)
}
fmt.Println("Found ", len(is), " instances")
}
This test display "Found 4 instances".
package main
import (
"fmt"
"github.com/goava/di"
)
type I interface{}
type A struct {}
type B struct {}
func main() {
c, err := di.New()
if err != nil {
panic(err)
}
c.Provide(func() interface{} {
return A{}
}, di.As(new(I)))
c.Provide(func() interface{} {
return B{}
}, di.As(new(I)))
is := make([]I, 0)
err = c.Resolve(&is)
if err != nil {
panic(err)
}
fmt.Println("Found ", len(is), " instances")
}
This test display "Found 2 instances"
The bug, if it is, occurs if constructor functions return a type which match those provided in di.As()
Sorry for the multiple questions. I did search through the previous issues, but could not find what I was looking for.
I'm trying to have a function that would return a variable number of dependencies to add to the DI container, but where the function also received some dependencies from previously declared di.Provide
. I saw this answer which seems to hint that it's possible for a di.Provide
to return di.Option
, but I cannot make it work.
Here is an example:
type moduleConfig struct {
di.Inject
cfg1 string `di:"cfg=m1.v1"`
cfg2 string `di:"cfg=m2.v1"`
}
func provideModules(cfg moduleConfig) di.Option {
//imagine some hypothetical conditions to decide which modules to create. 0-n modules can be created
//using moduleConfig to decide...
return di.Options(
di.Provide(m1.NewModule1, di.As(new(component.Module))),
di.Provide(m2.NewModule2, di.As(new(component.Module))),
)
}
func startModules(modules []component.Module) error {
//... do something with the declared modules
return nil
}
func main() {
c, err := di.New(
di.ProvideValue("aaa", di.Tags{"cfg": "m1.v1"}),
di.ProvideValue("bbb", di.Tags{"cfg": "m2.v1"}),
di.Provide(provideModules),
)
if err != nil {
panic(err)
}
err = c.Invoke(startModules)
if err != nil {
panic(err)
}
}
Does it possible to disable singleton behavior for some dependencies? Probably some ResolveOption
or ProvideOption
for that.
For example I want use goava/di container for testing with mocks. I have two different setups:
di.New(...)
with production implementation of interfaces which is used for release builds.di.New(...)
with mocks which replace external APIs implementations.Majority of mock libs assumes that each test recreate mock object but di container always returns same instance of mock object.
I can call di.New
for each test case but it is not looks good.
Hi, I really like the library but I'm a bit of a noob and don't get why this returns False? trace prints all is registered, but I'm not sure what is going on, it also fails when I try to get that or any registered struct, sample is copied from example.
func main() {
di.SetTracer(&di.StdTracer{})
// create container
c, err := di.New(
di.Provide(NewContext), // provide application context
di.Provide(NewServer), // provide http server
di.Provide(NewServeMux), // provide http serve mux
// controllers as []Controller group
di.Provide(NewOrderController, di.As(new(Controller))),
di.Provide(NewUserController, di.As(new(Controller))),
)
// handle container errors
if err != nil {
log.Fatal(err)
}
var server *http.Server
fmt.Println(c.Has(server)) // returns false ???
// invoke function
if err := c.Invoke(StartServer); err != nil {
log.Fatal(err)
}
}
Thanks.
The title :)
I can't make Go's playground to run this code since it's using a version of the project without Container available in the dependency graph.
package main
import (
"log"
"github.com/goava/di"
)
type Interface interface {
Do()
}
type Implementation1 struct{}
func (d Implementation1) Do() {}
type Implementation2 struct{}
func (d Implementation2) Do() {}
func newFactory(container *di.Container) (Interface, error) {
// Some decision logic which implementation to choose
var implementation *Implementation1
err := container.Resolve(&implementation)
if err != nil {
return nil, err
}
return implementation, nil
}
func newImplementation1() *Implementation1 {
println("called implementation 1")
return &Implementation1{}
}
func newImplementation2() *Implementation2 {
println("called implementation 2")
return &Implementation2{}
}
func main() {
container, err := di.New(
di.Provide(newFactory),
di.Provide(newImplementation1),
di.Provide(newImplementation2),
)
if err != nil {
log.Fatal(err)
}
var implementationByFactory Interface
err = container.Resolve(&implementationByFactory)
if err != nil {
log.Fatal(err)
}
var directImplementation *Implementation1
err = container.Resolve(&directImplementation)
if err != nil {
log.Fatal(err)
}
}
Output:
called implementation 1
called implementation 1
But expected only one call.
When calling "Invoke" function in multiple goroutine, it can lead to concurrent read and map write in this part of the code :
// check cycle verified
if !c.verified[param.Key()] {
err := checkCycles(c, param)
if err != nil {
return err
}
c.verified[param.Key()] = true
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.