jwells131313 / dargo Goto Github PK
View Code? Open in Web Editor NEWDependency Injection for GO
License: Other
Dependency Injection for GO
License: Other
Scopes with different lifecycles have problems when being injected into each other. For example, image a Context scope service being injected into a Singleton scope service. When the Context changes if you just inject the raw service it'll be wrong in the Singleton service. This is unfortunate, and the only real answer I can think of is the old JSR-330 style Provider. So, something like this:
type Foo struct {
BarProvider Provider inject:"Bar"
}
Where provider looks something like this maybe:
type Provider interface {
Get() (interface{}, error)
GetAll() ([]interface{}, error)
}
Might also want to use some of the concepts from IterableProvider in hk2
Immediate scope starts services right away
A Provider should be able to be injected even if there is no backing service, right?
Binder should have a method for binding an actual final service. Very convenient.
Probably called, like, "BindConstant"
The documentation in the README for Provider does not have enough information in it. Beef it up, maybe adding a small section for each API of Provider
What if the user doesn't like the inject:"whatever"
tag on their structs?
They can provide a CustomInjectionResolver and then use whatever sort of tag they want on the structure! This is how hk2 does this, and it does come in useful in many different scenarios.
The idea is named services in a specific namespace, and the first one that finds a tag wins. So in theory a user could override the system supplied "inject" custom injection resolver
Need a way to say an injection is optional. Maybe something like:
type Foo struct {
Foo Foo inject:"Foo,optional"
}
or something like that
Add a ValidationService for security, just like in hk2.
Once the ValidationService is in and documented I would say that's a point where we can declare a new release of dargo
Scenario:
Singleton Service A depends on Singleton Service B
Singleton Service B depends on Singleton Service C
Singleton Service C depends on Singleton Service A
All dependencies are annotated.
Problem:
Services are left uninitialized since they all depend on each other.
Expected Behaviour:
I expected, similar to other frameworks I used on a different languages, The services would be first initialized and created, and only then dependencies would be passed to each other, avoiding this behaviour that typically occurs on constructor injection only.
Workaround:
Remove the annotations. After injection container initializes all services, manually assign the dependencies between each service.
From docs:
Any depth of injection is supported (ServiceA can depend on ServiceB which depends on ServiceC and so on). A service can also depend on as many services as it would like (ServiceA can depend on service D, E and F etc). Howerver, services cannot have circular dependencies.
Creating larger than 7-depth services fails: https://go.dev/play/p/QwAmt1aIbiE
panic: 1. write lock is not held by this thread
2. an error occurred while getting the dependencies of default#Component3.1.5
3. an error occurred while getting the dependencies of default#Component4.1.6
4. an error occurred while getting the dependencies of default#Component5.1.7
5. an error occurred while getting the dependencies of default#Component6.1.8
6. an error occurred while getting the dependencies of default#Component7.1.9
7. an error occurred while getting the dependencies of default#Component8.1.10
goroutine 1 [running]:
main.main()
/tmp/sandbox741982879/prog.go:58 +0xa5
In real-project we also experienced IOC error: function called from non-goethe thread
and sometimes code just hangs on goethe lock acquire in gotethe cache/cache.go:243
.
If the depth of creation is smaller (ex. something existed previously), it works: https://go.dev/play/p/SVmmLqr3_6I
Lookups can be done a lot faster if we can know the Namespace and Name involved.
Modify Filter to supply these (they can return "" for any) and then keep descriptors indexed
by namespace/name internally
ServiceLocators have a named map. Shutdown does not currently remove themselves from that map, which is a leak of service locators. Fix that.
When running the following code available on Go Playground:
package main
import (
"fmt"
"github.com/jwells131313/dargo/ioc"
)
type ConfigA struct {
Flag1 int
Flag2 int
}
type ConfigB struct {
Flag3 int
Flag4 int
}
type Config struct {
ConfigA
ConfigB
}
type ServiceA struct {
ServiceConfig *ConfigA `inject:"Config"`
}
func main() {
locator, err := ioc.CreateAndBind("testing", func(binder ioc.Binder) error {
binder.Bind("Config", &Config{}).InScope(ioc.Singleton)
binder.Bind("ServiceA", &ServiceA{}).InScope(ioc.Singleton)
return nil
})
if err != nil {
return
}
// The next line fails
raw, err := locator.GetDService("ServiceA")
if err != nil {
fmt.Printf("ERROR %v+", err)
return
}
fmt.Printf("%v+", raw)
}
I get the error:
ERROR 1. reflect.Set: value of type *main.Config is not assignable to type *main.ConfigA
2. an error occurred while injecting the dependencies of default#ServiceA.1.4+
This makes sense because one can't do ConfigA = Config
, you have to do ConfigA = Config.ConfigA
. I am instead having to use Qualifiers to bind Config.ConfigA
to Config@ConfigA
etc.
This could be a case that the internal resolver can handle using Reflection.
I may be able to submit a PR for this if you could point me to where the best place to place this logic would be. I tried writing a custom resolver however, I needed to call GetDService
to get the Config
service in my custom resolver and the system errored out thinking there was a circular dependency.
Thank you
Users write... uh... code that sometimes panics.
Panics from user callbacks should not cause dargo API to panic. Instead they should all be handled properly.
All calls to things like ErrorService and ValidationService should be wrapped and all panics handled appropriately
I should be allowed to specify the namespace and qualifiers for injected services. Possibly something like this:
type Foo struct {
Bar Bar inject:"some/name/space#Bar@Red@Blue"
}
In the above case the namespace would be "some/name/space", the name would be "Bar" and it would want qualifiers "Red" and "Blue"
To have Immediate scope work we need a ConfigurationChangedListener service
The inject method will inject all known dependencies into the already existing structure without having Dargo manage the structure passed in. This is very useful for people just trying Dargo out who do not want to completely buy in but want to go "IoC" light.
See also hk2 inject
A Just-in-time injection resolver is a second-chance when a service cannot be found. It's very useful when integrating with other systems, or when building bridges between systems. It's also useful for remote services that might be found only at runtime.
Events allow services to communicate with each other without prior association.
It should be pluggable so that subscribers can get any sort of quality of service that the system needs as in the hk2 system
ErrorService has been added, but has not been documented. Document it.
Instance lifecycle listeners are very useful for debugging the internals of the dargo system and for other reasons. Note that instance listeners are different from Validation listeners because validation listeners are for lookups/injections of services (which may already exist). Instance listeners are different from Configuration listeners because configuration listeners are only for bind/unbind.
Instance listeners are called at specific points in the lifecycle of an actual service object:
PRE_PRODUCTION
POST_PRODUCTION
PRE_DESTRUCTION
The examples directory is getting bigger and bigger, I think it'd be better to add another layer so that it's easier to find examples specific to the feature you are interested in
It's nice and puts less stuff on the stack
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.