mirror of
https://github.com/mbierlee/poodinis.git
synced 2024-11-15 12:14:01 +01:00
Add constructor injection
This commit is contained in:
parent
81c6faed16
commit
ab765e0092
|
@ -147,7 +147,8 @@ synchronized class DependencyContainer {
|
||||||
return existingRegistration;
|
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();
|
newRegistration.singleInstance();
|
||||||
|
|
||||||
static if (!is(SuperType == ConcreteType)) {
|
static if (!is(SuperType == ConcreteType)) {
|
||||||
|
|
|
@ -11,8 +11,12 @@
|
||||||
|
|
||||||
module poodinis.factory;
|
module poodinis.factory;
|
||||||
|
|
||||||
|
import poodinis.container;
|
||||||
|
|
||||||
import std.typecons;
|
import std.typecons;
|
||||||
import std.exception;
|
import std.exception;
|
||||||
|
import std.traits;
|
||||||
|
import std.meta;
|
||||||
|
|
||||||
debug {
|
debug {
|
||||||
import std.string;
|
import std.string;
|
||||||
|
@ -39,6 +43,10 @@ class InstanceFactory {
|
||||||
private Object instance = null;
|
private Object instance = null;
|
||||||
private InstanceFactoryParameters _factoryParameters;
|
private InstanceFactoryParameters _factoryParameters;
|
||||||
|
|
||||||
|
this() {
|
||||||
|
factoryParameters = InstanceFactoryParameters();
|
||||||
|
}
|
||||||
|
|
||||||
public @property void factoryParameters(InstanceFactoryParameters factoryParameters) {
|
public @property void factoryParameters(InstanceFactoryParameters factoryParameters) {
|
||||||
if (factoryParameters.factoryMethod is null) {
|
if (factoryParameters.factoryMethod is null) {
|
||||||
factoryParameters.factoryMethod = &this.createInstance;
|
factoryParameters.factoryMethod = &this.createInstance;
|
||||||
|
@ -73,8 +81,65 @@ class InstanceFactory {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object createInstance() {
|
protected Object createInstance() {
|
||||||
enforce!InstanceCreationException(_factoryParameters.instanceType, "Instance type is not defined, cannot create instance without knowing its type.");
|
enforce!InstanceCreationException(_factoryParameters.instanceType, "Instance type is not defined, cannot create instance without knowing its type.");
|
||||||
return _factoryParameters.instanceType.create();
|
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
|
// Test register concrete type
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new shared DependencyContainer();
|
auto container = new shared DependencyContainer();
|
||||||
|
@ -651,4 +662,18 @@ version(unittest) {
|
||||||
auto instances = container.resolveAll!TestInterface(ResolveOption.noResolveException);
|
auto instances = container.resolveAll!TestInterface(ResolveOption.noResolveException);
|
||||||
assert(instances.length == 0);
|
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 poodinis;
|
||||||
|
|
||||||
|
import std.exception;
|
||||||
|
|
||||||
version(unittest) {
|
version(unittest) {
|
||||||
|
|
||||||
interface TestInterface {}
|
interface TestInterface {}
|
||||||
|
@ -15,6 +17,68 @@ version(unittest) {
|
||||||
public string someContent = "";
|
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
|
// Test instance factory with singletons
|
||||||
unittest {
|
unittest {
|
||||||
auto factory = new InstanceFactory();
|
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 !is null, "No instance was created by factory or could not be cast to expected type");
|
||||||
assert(instance.someContent == "Ducks!");
|
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