Add @Qualifier UDA for qualifying members typed by supertype

This commit is contained in:
Mike Bierlee 2014-12-13 22:06:11 +01:00
parent 9c749c0cb6
commit 1166d2811a
3 changed files with 98 additions and 16 deletions

View file

@ -130,6 +130,14 @@ container.register!(Color, Blue);
container.register!(Color, Red); container.register!(Color, Red);
auto blueInstance = container.resolve!(Color, Blue); auto blueInstance = container.resolve!(Color, Blue);
``` ```
If you want to autowire a type registered to multiple concrete types, use the @Qualifier UDA:
```d
class BluePaint {
@Autowire
@Qualifier!Blue
public Color color;
}
```
If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a ResolveException is throw stating that there are multiple candidates for the type to be resolved. If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a ResolveException is throw stating that there are multiple candidates for the type to be resolved.
Known issues Known issues

View file

@ -18,30 +18,61 @@ debug {
class Autowire{}; class Autowire{};
struct Qualifier(T) {
T qualifier;
}
alias Autowired = Autowire; alias Autowired = Autowire;
public void autowire(Type)(DependencyContainer container, Type instance) { public void autowire(Type)(DependencyContainer container, Type instance) {
// For the love of god, refactor this!
debug { debug {
auto memberType = typeid(Type); auto instanceType = typeid(Type);
auto instanceAddress = &instance; auto instanceAddress = &instance;
writeln(format("DEBUG: Autowiring members of [%s@%s]", memberType, instanceAddress)); writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress));
} }
foreach (member ; __traits(allMembers, Type)) { foreach (member ; __traits(allMembers, Type)) {
static if(__traits(compiles, __traits(getMember, Type, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, Type, member)))) { static if(__traits(compiles, __traits(getMember, Type, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, Type, member)))) {
foreach(attribute; __traits(getAttributes, __traits(getMember, Type, member))) { foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, Type, member))) {
static if (is(attribute : Autowire)) { static if (is(autowireAttribute : Autowire)) {
if (__traits(getMember, instance, member) is null) { if (__traits(getMember, instance, member) is null) {
alias TypeTuple!(__traits(getMember, instance, member)) memberReference; alias TypeTuple!(__traits(getMember, instance, member)) memberReference;
auto autowirableInstance = container.resolve!(typeof(memberReference)); alias MemberType = typeof(memberReference)[0];
debug { debug {
auto autowirableType = typeid(typeof(memberReference[0])); string qualifiedInstanceTypeString = typeid(MemberType).toString;
auto autowireableAddress = &autowirableInstance;
writeln(format("DEBUG: Autowire instance [%s@%s] to [%s@%s].%s", autowirableType, autowireableAddress, memberType, instanceAddress, member));
} }
__traits(getMember, instance, member) = autowirableInstance; MemberType qualifiedInstance;
auto resolvedThroughQualifier = false;
foreach (qualifierAttribute; __traits(getAttributes, __traits(getMember, Type, member))) {
static if (is(qualifierAttribute == Qualifier!T, T)) {
alias QualifierType = typeof(qualifierAttribute.qualifier);
qualifiedInstance = container.resolve!(typeof(memberReference), QualifierType);
debug {
qualifiedInstanceTypeString = typeid(QualifierType).toString;
} }
resolvedThroughQualifier = true;
break;
}
}
if (!resolvedThroughQualifier) {
qualifiedInstance = container.resolve!(typeof(memberReference));
}
__traits(getMember, instance, member) = qualifiedInstance;
debug {
auto qualifiedInstanceAddress = &qualifiedInstance;
writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", qualifiedInstanceTypeString, qualifiedInstanceAddress, instanceType, instanceAddress, member));
}
}
break;
} }
} }
} }

View file

@ -10,8 +10,7 @@ import poodinis.autowire;
import std.exception; import std.exception;
version(unittest) { version(unittest) {
class ComponentA { class ComponentA {}
}
class ComponentB { class ComponentB {
public @Autowire ComponentA componentA; public @Autowire ComponentA componentA;
@ -21,11 +20,9 @@ version(unittest) {
} }
} }
interface InterfaceA { interface InterfaceA {}
}
class ComponentC : InterfaceA { class ComponentC : InterfaceA {}
}
class ComponentD { class ComponentD {
public @Autowire InterfaceA componentC = null; public @Autowire InterfaceA componentC = null;
@ -62,6 +59,24 @@ version(unittest) {
} }
} }
class ComponentX : InterfaceA {}
class MonkeyShine {
@Autowire
@Qualifier!ComponentX
public InterfaceA component;
}
class BootstrapBootstrap {
@Autowire
@Qualifier!ComponentX
public InterfaceA componentX;
@Autowire
@Qualifier!ComponentC
public InterfaceA componentC;
}
// Test autowiring concrete type to existing instance // Test autowiring concrete type to existing instance
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -132,4 +147,32 @@ version(unittest) {
assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed"); assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed");
} }
// Test autowire class with qualifier
unittest {
auto container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto componentX = container.resolve!(InterfaceA, ComponentX);
auto monkeyShine = new MonkeyShine();
container.autowire(monkeyShine);
assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed");
}
// Test autowire class with multiple qualifiers
unittest {
auto container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto componentC = container.resolve!(InterfaceA, ComponentC);
auto componentX = container.resolve!(InterfaceA, ComponentX);
auto bootstrapBootstrap = new BootstrapBootstrap();
container.autowire(bootstrapBootstrap);
assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed");
assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed");
}
} }