mirror of
https://github.com/mbierlee/poodinis.git
synced 2024-11-15 04:04:01 +01:00
Add constructor injection
This commit is contained in:
parent
81c6faed16
commit
ab765e0092
|
@ -147,7 +147,8 @@ synchronized class DependencyContainer {
|
|||
return existingRegistration;
|
||||
}
|
||||
|
||||
auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, new InstanceFactory(), this);
|
||||
auto instanceFactory = new ConstructorInjectingInstanceFactory!ConcreteType(this);
|
||||
auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, instanceFactory, this);
|
||||
newRegistration.singleInstance();
|
||||
|
||||
static if (!is(SuperType == ConcreteType)) {
|
||||
|
|
|
@ -11,8 +11,12 @@
|
|||
|
||||
module poodinis.factory;
|
||||
|
||||
import poodinis.container;
|
||||
|
||||
import std.typecons;
|
||||
import std.exception;
|
||||
import std.traits;
|
||||
import std.meta;
|
||||
|
||||
debug {
|
||||
import std.string;
|
||||
|
@ -39,6 +43,10 @@ class InstanceFactory {
|
|||
private Object instance = null;
|
||||
private InstanceFactoryParameters _factoryParameters;
|
||||
|
||||
this() {
|
||||
factoryParameters = InstanceFactoryParameters();
|
||||
}
|
||||
|
||||
public @property void factoryParameters(InstanceFactoryParameters factoryParameters) {
|
||||
if (factoryParameters.factoryMethod is null) {
|
||||
factoryParameters.factoryMethod = &this.createInstance;
|
||||
|
@ -73,8 +81,65 @@ class InstanceFactory {
|
|||
return instance;
|
||||
}
|
||||
|
||||
private Object createInstance() {
|
||||
protected Object createInstance() {
|
||||
enforce!InstanceCreationException(_factoryParameters.instanceType, "Instance type is not defined, cannot create instance without knowing its type.");
|
||||
return _factoryParameters.instanceType.create();
|
||||
}
|
||||
}
|
||||
|
||||
class ConstructorInjectingInstanceFactory(InstanceType) : InstanceFactory {
|
||||
private shared DependencyContainer container;
|
||||
|
||||
this(shared DependencyContainer container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
private static string createArgumentList(Params...)() {
|
||||
string argumentList = "";
|
||||
foreach(param; Params) {
|
||||
if (argumentList.length > 0) {
|
||||
argumentList ~= ",";
|
||||
}
|
||||
|
||||
argumentList ~= "container.resolve!" ~ param.stringof;
|
||||
}
|
||||
return argumentList;
|
||||
}
|
||||
|
||||
private static bool parametersAreValid(Params...)() {
|
||||
bool isValid = true;
|
||||
foreach(param; Params) {
|
||||
if (isBuiltinType!param) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
protected override Object createInstance() {
|
||||
enforce!InstanceCreationException(container, "A dependency container is not defined. Cannot perform constructor injection without one.");
|
||||
|
||||
Object instance = null;
|
||||
static if (__traits(compiles, __traits(getOverloads, InstanceType, `__ctor`))) {
|
||||
foreach(ctor ; __traits(getOverloads, InstanceType, `__ctor`)) {
|
||||
static if (parametersAreValid!(Parameters!ctor)) {
|
||||
mixin(`
|
||||
import ` ~ moduleName!InstanceType ~ `;
|
||||
instance = new ` ~ fullyQualifiedName!InstanceType ~ `(` ~ createArgumentList!(Parameters!ctor) ~ `);
|
||||
`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (instance is null) {
|
||||
instance = typeid(InstanceType).create();
|
||||
}
|
||||
|
||||
enforce!InstanceCreationException(instance !is null, "Unable to create instance of type" ~ InstanceType.stringof ~ ", does it have injectable constructors?");
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,6 +180,17 @@ version(unittest) {
|
|||
}
|
||||
}
|
||||
|
||||
class Cocktail {
|
||||
@Autowire
|
||||
public Moolah moolah;
|
||||
|
||||
public Red red;
|
||||
|
||||
this(Red red) {
|
||||
this.red = red;
|
||||
}
|
||||
}
|
||||
|
||||
// Test register concrete type
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
|
@ -651,4 +662,18 @@ version(unittest) {
|
|||
auto instances = container.resolveAll!TestInterface(ResolveOption.noResolveException);
|
||||
assert(instances.length == 0);
|
||||
}
|
||||
|
||||
// Test autowired, constructor injected class
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Red;
|
||||
container.register!Moolah;
|
||||
container.register!Cocktail;
|
||||
|
||||
auto instance = container.resolve!Cocktail;
|
||||
|
||||
assert(instance !is null);
|
||||
assert(instance.moolah is container.resolve!Moolah);
|
||||
assert(instance.red is container.resolve!Red);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
import poodinis;
|
||||
|
||||
import std.exception;
|
||||
|
||||
version(unittest) {
|
||||
|
||||
interface TestInterface {}
|
||||
|
@ -15,6 +17,68 @@ version(unittest) {
|
|||
public string someContent = "";
|
||||
}
|
||||
|
||||
class SomeOtherClassThen {
|
||||
}
|
||||
|
||||
class ClassWithConstructor {
|
||||
public TestImplementation testImplementation;
|
||||
|
||||
this(TestImplementation testImplementation) {
|
||||
this.testImplementation = testImplementation;
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithMultipleConstructors {
|
||||
public SomeOtherClassThen someOtherClassThen;
|
||||
public TestImplementation testImplementation;
|
||||
|
||||
this(SomeOtherClassThen someOtherClassThen) {
|
||||
this.someOtherClassThen = someOtherClassThen;
|
||||
}
|
||||
|
||||
this(SomeOtherClassThen someOtherClassThen, TestImplementation testImplementation) {
|
||||
this.someOtherClassThen = someOtherClassThen;
|
||||
this.testImplementation = testImplementation;
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithConstructorWithMultipleParameters {
|
||||
public SomeOtherClassThen someOtherClassThen;
|
||||
public TestImplementation testImplementation;
|
||||
|
||||
this(SomeOtherClassThen someOtherClassThen, TestImplementation testImplementation) {
|
||||
this.someOtherClassThen = someOtherClassThen;
|
||||
this.testImplementation = testImplementation;
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithPrimitiveConstructor {
|
||||
public SomeOtherClassThen someOtherClassThen;
|
||||
|
||||
this(string willNotBePicked) {
|
||||
}
|
||||
|
||||
this(SomeOtherClassThen someOtherClassThen) {
|
||||
this.someOtherClassThen = someOtherClassThen;
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithEmptyConstructor {
|
||||
public SomeOtherClassThen someOtherClassThen;
|
||||
|
||||
this() {
|
||||
}
|
||||
|
||||
this(SomeOtherClassThen someOtherClassThen) {
|
||||
this.someOtherClassThen = someOtherClassThen;
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithNonInjectableConstructor {
|
||||
this(string myName) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test instance factory with singletons
|
||||
unittest {
|
||||
auto factory = new InstanceFactory();
|
||||
|
@ -74,4 +138,78 @@ version(unittest) {
|
|||
assert(instance !is null, "No instance was created by factory or could not be cast to expected type");
|
||||
assert(instance.someContent == "Ducks!");
|
||||
}
|
||||
|
||||
// Test injecting constructor of class
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestImplementation;
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithConstructor(container);
|
||||
auto instance = cast(ClassWithConstructor) factory.getInstance();
|
||||
|
||||
assert(instance !is null);
|
||||
assert(instance.testImplementation is container.resolve!TestImplementation);
|
||||
}
|
||||
|
||||
// Test injecting constructor of class with multiple constructors injects the first candidate
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!SomeOtherClassThen;
|
||||
container.register!TestImplementation;
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithMultipleConstructors(container);
|
||||
auto instance = cast(ClassWithMultipleConstructors) factory.getInstance();
|
||||
|
||||
assert(instance !is null);
|
||||
assert(instance.someOtherClassThen is container.resolve!SomeOtherClassThen);
|
||||
assert(instance.testImplementation is null);
|
||||
}
|
||||
|
||||
// Test injecting constructor of class with multiple constructor parameters
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!SomeOtherClassThen;
|
||||
container.register!TestImplementation;
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithConstructorWithMultipleParameters(container);
|
||||
auto instance = cast(ClassWithConstructorWithMultipleParameters) factory.getInstance();
|
||||
|
||||
assert(instance !is null);
|
||||
assert(instance.someOtherClassThen is container.resolve!SomeOtherClassThen);
|
||||
assert(instance.testImplementation is container.resolve!TestImplementation);
|
||||
}
|
||||
|
||||
// Test injecting constructor of class with primitive constructor parameters
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!SomeOtherClassThen;
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithPrimitiveConstructor(container);
|
||||
auto instance = cast(ClassWithPrimitiveConstructor) factory.getInstance();
|
||||
|
||||
assert(instance !is null);
|
||||
assert(instance.someOtherClassThen is container.resolve!SomeOtherClassThen);
|
||||
}
|
||||
|
||||
|
||||
// Test injecting constructor of class with empty constructor will skip injection
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithEmptyConstructor(container);
|
||||
auto instance = cast(ClassWithEmptyConstructor) factory.getInstance();
|
||||
|
||||
assert(instance !is null);
|
||||
assert(instance.someOtherClassThen is null);
|
||||
}
|
||||
|
||||
// Test injecting constructor of class with no candidates fails
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithNonInjectableConstructor(container);
|
||||
|
||||
assertThrown!InstanceCreationException(factory.getInstance());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue