Move injection initializer to registration scope

This commit is contained in:
Mike Bierlee 2021-04-29 01:11:34 +03:00
parent 89512e0cb0
commit 8f4a64cf4c
6 changed files with 35 additions and 50 deletions

View file

@ -1,5 +1,8 @@
Poodinis Changelog Poodinis Changelog
================== ==================
**Version 8.1.0-beta.3**
* CHANGE injection initializers to be defined as a registration scope instead of via Container.register(). See initializedBy().
**Version 8.1.0-beta.2** **Version 8.1.0-beta.2**
* FIX inheritance type template in custom instance creator (PR #29) * FIX inheritance type template in custom instance creator (PR #29)

View file

@ -50,22 +50,28 @@ Dependency Creation Behaviour
You can control how a dependency is resolved by specifying a creation scope during registration. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available: You can control how a dependency is resolved by specifying a creation scope during registration. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available:
* Resolve a dependency using a single instance (default): * Resolve a dependency using a single instance (default):
```d ```d
dependencies.register!ExampleClass.singleInstance(); dependencies.register!ExampleClass.singleInstance();
``` ```
* Resolve a dependency with a new instance each time it is resolved:
* Resolve a dependency with a new instance each time it is resolved:
```d ```d
dependencies.register!ExampleClass.newInstance(); dependencies.register!ExampleClass.newInstance();
``` ```
* Resolve a dependency using a pre-existing instance
* Resolve a dependency using a pre-existing instance:
```d ```d
auto preExistingInstance = new ExampleClass(); auto preExistingInstance = new ExampleClass();
dependencies.register!ExampleClass.existingInstance(preExistingInstance); dependencies.register!ExampleClass.existingInstance(preExistingInstance);
``` ```
* Resolve a dependency using a custom initializer delegate:
```d
dependencies.register!ExampleClass.initializedBy({
return new ExampleClass();
});
```
Automatic Injection Automatic Injection
---------- ----------
The real value of any dependency injection framework comes from its ability to automatically inject dependencies. Poodinis supports automatic injection either through autowiring members annotated with the `@Autowire` UDA or through constructor injection. The real value of any dependency injection framework comes from its ability to automatically inject dependencies. Poodinis supports automatic injection either through autowiring members annotated with the `@Autowire` UDA or through constructor injection.

View file

@ -15,7 +15,7 @@ class Doohickey
void main() void main()
{ {
auto dependencies = new shared DependencyContainer(); auto dependencies = new shared DependencyContainer();
dependencies.register!Doohickey({ dependencies.register!Doohickey.initializedBy({
writeln("Creating Doohickey via initializer delegate."); writeln("Creating Doohickey via initializer delegate.");
return new Doohickey(); return new Doohickey();
}); });

View file

@ -30,8 +30,6 @@ debug {
import std.stdio; import std.stdio;
} }
alias InjectionInitializer(T) = T delegate();
/** /**
* Exception thrown when errors occur while resolving a type in a dependency container. * Exception thrown when errors occur while resolving a type in a dependency container.
*/ */
@ -149,11 +147,6 @@ synchronized class DependencyContainer {
return register!(ConcreteType, ConcreteType)(options); return register!(ConcreteType, ConcreteType)(options);
} }
Registration register(ConcreteType)(InjectionInitializer!ConcreteType initializer,
RegistrationOption options = RegistrationOption.none) {
return register!(ConcreteType, ConcreteType)(initializer, options);
}
/** /**
* Register a dependency by super type. * Register a dependency by super type.
* *
@ -201,45 +194,6 @@ synchronized class DependencyContainer {
return newRegistration; return newRegistration;
} }
/**
* TODO: Deduplicate code
*/
Registration register(SuperType, ConcreteType : SuperType)(InjectionInitializer!SuperType initializer,
RegistrationOption options = RegistrationOption.none)
if (is(ConcreteType == class)) {
TypeInfo registeredType = typeid(SuperType);
TypeInfo_Class concreteType = typeid(ConcreteType);
debug(poodinisVerbose) {
writeln(format("DEBUG: Register type %s (as %s)", concreteType.toString(), registeredType.toString()));
}
auto existingRegistration = getExistingRegistration(registeredType, concreteType);
if (existingRegistration) {
return existingRegistration;
}
InstanceFactory instanceFactory = new class InstanceFactory {
protected override Object createInstance() {
return cast(Object)initializer();
}
};
auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, instanceFactory, this);
newRegistration.singleInstance();
static if (!is(SuperType == ConcreteType)) {
if (!hasOption(options, persistentRegistrationOptions, RegistrationOption.doNotAddConcreteTypeRegistration)) {
auto concreteTypeRegistration = register!ConcreteType;
concreteTypeRegistration.linkTo(newRegistration);
}
}
registrations[registeredType] ~= cast(shared(Registration)) newRegistration;
return newRegistration;
}
private bool hasOption(OptionType)(OptionType options, OptionType persistentOptions, OptionType option) { private bool hasOption(OptionType)(OptionType options, OptionType persistentOptions, OptionType option) {
return ((options | persistentOptions) & option) != 0; return ((options | persistentOptions) & option) != 0;
} }

View file

@ -105,6 +105,17 @@ public Registration existingInstance(Registration registration, Object instance)
return registration; return registration;
} }
/**
* Scopes registrations to create instances using the given initializer delegate.
*/
public Registration initializedBy(T : Object)(Registration registration, T delegate() initializer) {
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(registration.instanceType, CreatesSingleton.no, null, {
return cast(Object) initializer();
});
return registration;
}
public string toConcreteTypeListString(Registration[] registrations) { public string toConcreteTypeListString(Registration[] registrations) {
auto concreteTypeListString = ""; auto concreteTypeListString = "";
foreach (registration ; registrations) { foreach (registration ; registrations) {

View file

@ -80,6 +80,17 @@ version(unittest) {
assert(expectedInstance is actualInstance, "Resolved instance from existing instance scope is not the same as the registered instance"); assert(expectedInstance is actualInstance, "Resolved instance from existing instance scope is not the same as the registered instance");
} }
// Test creating instance via customer initializer on resolve
unittest {
auto container = new shared DependencyContainer();
auto expectedInstance = new TestClass();
container.register!TestClass.initializedBy({
return expectedInstance;
});
auto actualInstance = container.resolve!TestClass;
assert(expectedInstance is actualInstance, "Resolved instance does not come from the custom initializer");
}
// Test autowire resolved instances // Test autowire resolved instances
unittest { unittest {
auto container = new shared DependencyContainer(); auto container = new shared DependencyContainer();