Add post construction

This commit is contained in:
Mike Bierlee 2016-12-17 21:14:16 +01:00
parent 7ba8e545f6
commit fbef764b48
3 changed files with 113 additions and 9 deletions

View file

@ -3,6 +3,7 @@ Poodinis Changelog
**Version NEXT**
* ADD value injection. Members with UDA @Value will be attempted to be injected with a value-type. See tutorial and examples for more info.
* ADD Phobos 2.072.1 forwards-compatibility for D/Phobos 2.066.1. This means you can use Poodinis with D/Phobos 2.066.1 compatible compilers such as GDC.
* ADD @PostConstruct UDA for marking methods which should be called after a dependency is resolved and autowired.
* FIX nullpointer exception in instance factory when debugging with poodinisVerbose
**Version 7.0.1**

View file

@ -13,19 +13,21 @@
module poodinis.container;
import std.string;
import std.algorithm;
import std.concurrency;
debug {
import std.stdio;
}
import poodinis.registration;
import poodinis.autowire;
import poodinis.context;
import poodinis.factory;
import poodinis.valueinjection;
import poodinis.polyfill;
import std.string;
import std.algorithm;
import std.concurrency;
import std.traits;
debug {
import std.stdio;
}
/**
* Exception thrown when errors occur while resolving a type in a dependency container.
@ -80,6 +82,15 @@ public enum ResolveOption {
noResolveException = 1 << 1
}
/**
* Methods marked with this UDA within dependencies are called after that dependency
* is constructed by the dependency container.
*
* Multiple methods can be marked and will all be called after construction. The order in which
* methods are called is undetermined. Methods should have the signature void(void).
*/
struct PostConstruct {}
/**
* The dependency container maintains all dependencies registered with it.
*
@ -293,7 +304,9 @@ synchronized class DependencyContainer {
Registration registration = getQualifiedRegistration(resolveType, qualifierType, cast(Registration[]) *candidates);
try {
return resolveAutowiredInstance!QualifierType(registration);
QualifierType newInstance = resolveAutowiredInstance!QualifierType(registration);
callPostConstructors(newInstance);
return newInstance;
} catch (ValueInjectionException e) {
throw new ResolveException(e, resolveType);
}
@ -363,6 +376,16 @@ synchronized class DependencyContainer {
return getRegistration(candidates, qualifierType);
}
private void callPostConstructors(Type)(Type instance) {
foreach (memberName; __traits(allMembers, Type)) {
static if (__traits(getProtection, __traits(getMember, instance, memberName)) == "public"
&& isCallable!(__traits(getMember, instance, memberName))
&& hasUDA!(__traits(getMember, instance, memberName), PostConstruct)) {
__traits(getMember, instance, memberName)();
}
}
}
/**
* Clears all dependency registrations managed by this container.
*/

View file

@ -162,6 +162,44 @@ version(unittest) {
this(Ola ola) {}
}
class PostConstructionDependency {
public bool postConstructWasCalled = false;
@PostConstruct
public void callMeMaybe() {
postConstructWasCalled = true;
}
}
class ChildOfPostConstruction : PostConstructionDependency {}
interface ThereWillBePostConstruction {
@PostConstruct
void constructIt();
}
class ButThereWontBe : ThereWillBePostConstruction {
public bool postConstructWasCalled = false;
public override void constructIt() {
postConstructWasCalled = true;
}
}
class PostConstructWithAutowiring {
@Autowire
private PostConstructionDependency dependency;
@Value("")
private int theNumber = 1;
@PostConstruct
public void doIt() {
assert(theNumber == 8783);
assert(dependency !is null);
}
}
// Test register concrete type
unittest {
auto container = new shared DependencyContainer();
@ -636,4 +674,46 @@ version(unittest) {
container.register!Hello;
container.resolve!Hello;
}
// Test PostConstruct method is called after resolving a dependency
unittest {
auto container = new shared DependencyContainer();
container.register!PostConstructionDependency;
auto instance = container.resolve!PostConstructionDependency;
assert(instance.postConstructWasCalled == true);
}
// Test PostConstruct of base type is called
unittest {
auto container = new shared DependencyContainer();
container.register!ChildOfPostConstruction;
auto instance = container.resolve!ChildOfPostConstruction;
assert(instance.postConstructWasCalled == true);
}
// Test PostConstruct of class implementing interface is not called
unittest {
auto container = new shared DependencyContainer();
container.register!ButThereWontBe;
auto instance = container.resolve!ButThereWontBe;
assert(instance.postConstructWasCalled == false);
}
// Test postconstruction happens after autowiring and value injection
unittest {
class IntInjector : ValueInjector!int {
int get(string key) {
return 8783;
}
}
auto container = new shared DependencyContainer();
container.register!(ValueInjector!int, IntInjector);
container.register!PostConstructionDependency;
container.register!PostConstructWithAutowiring;
auto instance = container.resolve!PostConstructWithAutowiring;
}
}