Fix autowiring deep circular dependencies

This commit is contained in:
Mike Bierlee 2014-06-04 00:16:24 +02:00
parent b88745130a
commit b66b9d5def
3 changed files with 47 additions and 14 deletions

View file

@ -123,12 +123,11 @@ assert(instance.dependency !is null);
If an interface is to be autowired, you must register a concrete class by interface. Any class registered by concrete type can only be injected when a dependency on a concrete type is autowired.
###Circular dependencies
Poodinis can autowire circular dependencies to a certain degree. See known issues for current limitations on this. For now you might want to consider if your design really needs circular dependencies.
Poodinis can autowire circular dependencies when they are registered with singleInstance or existingInstance registration scopes. See Known issues for the limitations on newInstance scopes.
Known issues
------------
* Circular dependencies down the dependency tree still fails if the dependencies don't refer to the type being resolved initially.
* Due to preventive measures of recursion issues in circular dependencies, registrations which are supposed to yield new instances will not autowire classes for which a recursive resolve is detected.
* Due to preventive measures of recursion issues in circular dependencies, registrations which are supposed to yield new instances will not autowire classes for which a circular dependency is detected. A new instance will be resolved but the instance's members will not be autowired.
Future Work
-----------

View file

@ -9,6 +9,7 @@ module poodinis.container;
import std.string;
import std.array;
import std.algorithm;
public import poodinis.registration;
public import poodinis.autowire;
@ -31,7 +32,7 @@ class Container {
private Registration[TypeInfo] registrations;
private Registration* registrationBeingResolved;
private Registration*[] autowireStack;
public Registration register(ConcreteType)() {
return register!(ConcreteType, ConcreteType)();
@ -54,18 +55,12 @@ class Container {
throw new ResolveException("Type not registered.", resolveType);
}
auto initialResolve = false;
if (registrationBeingResolved is null) {
registrationBeingResolved = registration;
initialResolve = true;
}
RegistrationType instance = cast(RegistrationType) registration.getInstance();
if (initialResolve || registrationBeingResolved !is registration) {
if (!autowireStack.canFind(registration)) {
autowireStack ~= registration;
this.autowire!(RegistrationType)(instance);
}
if (initialResolve) {
registrationBeingResolved = null;
autowireStack.popBack();
}
return instance;

View file

@ -58,6 +58,21 @@ version(unittest) {
public Eenie eenie;
}
class Ittie {
@Autowire
public Bittie bittie;
}
class Bittie {
@Autowire
public Banana banana;
}
class Banana {
@Autowire
public Bittie bittie;
}
// Test register concrete type
unittest {
auto container = new Container();
@ -185,4 +200,28 @@ version(unittest) {
assert(eenie.meenie.moe.eenie is eenie, "Autowiring third-degree circular dependency failed");
}
// Test autowiring deep circular dependencies
unittest {
auto container = new Container();
container.register!Ittie;
container.register!Bittie;
container.register!Banana;
auto ittie = container.resolve!Ittie;
assert(ittie.bittie is ittie.bittie.banana.bittie, "Autowiring deep dependencies failed.");
}
// Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time
unittest {
auto container = new Container();
container.register!(Ittie).newInstance();
container.register!(Bittie).newInstance();
container.register!(Banana).newInstance();
auto ittie = container.resolve!Ittie;
assert(ittie.bittie.banana.bittie.banana is null, "Autowiring deep dependencies with newInstance scope autowired a reoccuring type.");
}
}