monnoroch / go-inject Goto Github PK
View Code? Open in Web Editor NEWA dependency injection system for Go inspired by Guice.
License: MIT License
A dependency injection system for Go inspired by Guice.
License: MIT License
This is used to override annotations to avoid duplication of providers.
For example, some module(waitModule
) extends the another provider of another module(serverModule
), performing waiting, but keeping the exposed provider signature.
And is also necessary to allow to create several instances of this module in the one injector.
In the following example, NextAnonimousAnnotatation ()
generates an anonymous unique type, that used in private scope of the waitModule
module.
type server struct {}
type serverModule struct {
endpoint string
}
func (self serverModule) ProvideEndpoint() (string, server) {
return self.endpoint, server{}
}
type readyServer struct {}
type waitModule struct {}
func (_ waitModule) ProvideValue(endpoint string, _ server) (string, readyServer, error) {
return endpoint, readyServer{}, waitEndpoint(endpoint)
}
func ModuleWithReadyServer(annotation inject.Annotation) inject.Module {
privateServerAnnotation := NextAnonimousAnnotatation()
return inject.CombineModules(
rewrite.RewriteAnnotations(serverModule{}, map[inject.Annotation]inject.Annotation{
server{}: privateServerAnnotation,
}),
rewrite.RewriteAnnotations(waitModule{}, map[inject.Annotation]inject.Annotation{
server{}: privateServerAnnotation,
readyServer{}: annotation,
}),
}
type server1 struct {}
type server2 struct {}
injector, err := inject.InjectorOf(
ModuleWithReadyServer(server1{endpoint:"server1.com"}),
ModuleWithReadyServer(server2{endpoint:"server1.com"}),
)
endpoint1 := injector.MustGet(new(string), server1{}).(string)
endpoint2 := injector.MustGet(new(string), server2{}).(string)
We would like to define multiple providers of the same type with different annotations and then inject them all.
For example, consider a Checker
interface for checking for a condition:
package checker
type Checker interface {
Check() bool
}
type funcChecker struct {
checker func() bool
}
func NewChecker(checker func() bool) Checker {
return funcChecker{checker}
}
One could install multiple modules, that potentially don't know about each other:
package p1
func (self module1) ProvideChecker() (checker.Checker, annotation1) {
return checker.NewChecker(func() bool { return true }), annotation1{}
}
...
package p2
func (self module2) ProvideChecker() (checker.Checker, annotation2) {
return checker.NewChecker(func() bool { return false }), annotation2{}
}
We want it to be possible to inject all installed checkers as an array:
package p3
type Component {
checkers []checker.Checker
}
func (self module) ProvideComponent(
checkers []checker.Checker, // doesn't work -- no annotation
) (Component, annotation3) {
return Component{checkers}, annotation3{}
}
This is usually needed for framework-type libraries that want, for example, gather all server dependencies in a list and check that they all are ready before starting the server itself.
I propose creating an annotation inject.All
that would allow depending on the array of all values of the same type, like so:
func (self module) ProvideComponent(
checkers []checker.Checker, inject.All,
) (Component, annotation3) {
return Component{checkers}, annotation3{}
}
No additional providers are needed, the injector knows about this special annotation and generates an appropriate provider that loops through all available keys with the required type, instantiates their values and injects the array with all of them.
As an alternative that can be implemented without changing the core, we could create a listinject.ListModule
module with dynamic providers that would be able to like so:
injector, _ := inject.InjectorOf(
module1{},
module2{},
listinject.ListModule().
ForType(new(checker.Checker)).
WithAnnotations(annotation1{}, annotation2{}).
WithOutputAnnotation(annotation3{}),
)
And then inject the list as:
func (self module) ProvideComponent(
checkers []checker.Checker, annotation3,
) (Component, annotation3) {
return Component{checkers}, annotation3{}
}
Pros:
Cons:
Goal:
type injectedAnnotation struct{}
type grpcClientModule struct {
annotation inject.Annotation
}
func (self testInjectedAnnotationsModule) ProvideClient(
endpoint string, _ injectedAnnotation,
) (int, injectedAnnotation) {
return grpc.NewClientWithEndpoint(endpoint), injectedAnnotation{}
}
func (self testInjectedAnnotationsModule) ProvideAnnotation() (inject.Annotation, injectedAnnotation) {
return self.annotation, injectedAnnotation{}
}
type Frontend struct {}
type Auth struct {}
type endpointsModule struct {}
func (self endpointsModule) ProvideFrontendEndpoint() (string, Frontend) {
return "frontend:80", Frontend{}
}
func (self endpointsModule) ProvideAuthEndpoint() (string, Auth) {
return "auth:80", Auth{}
}
type MyServer struct {
AuthClient grpc.Client (@Auth) // these annotations are pseudo-code
FrontendClient grpc.Client (@Frontend)
}
func main() {
injector = inject.InjectorOf(
grpcClientModule{
annotation: Frontend{},
}, grpcClientModule{
annotation: Auth{},
}, endpointsModule{},
inject.AutoInjectModule(new(MyServer)),
)
// Тote that we never wrote the MyServer provider manually.
myServer := injector.MustGet(new(MyServer), inject.AutoInject{}).(MyServer)
}
The core problem is the annotation syntax.
Right now this makes everything fail without an error message if a provider panics. Either:
The first option is safer for forward-compatibility, so let's do that real quick, but ultimately this need to be thought through.
Dynamic annotations are a poor idea:
We should replace them with a more generic feature that will also allow implementing auto injection modules and other user-defined extensions: dynamic provider modules. Design TBD.
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.