mirror of
https://github.com/mbierlee/poodinis.git
synced 2024-11-15 04:04:01 +01:00
Add pre-destruction
This commit is contained in:
parent
fbef764b48
commit
b6dea95c0d
|
@ -4,6 +4,8 @@ Poodinis Changelog
|
|||
* 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.
|
||||
* ADD @PreDestroy UDA for marking methods which should be called when the container loses a dependency's registration. It is called when
|
||||
removeRegistration or clearAllRegistrations is called. It is also called when the container is destroyed.
|
||||
* FIX nullpointer exception in instance factory when debugging with poodinisVerbose
|
||||
|
||||
**Version 7.0.1**
|
||||
|
|
|
@ -257,9 +257,23 @@ class AutowiredRegistration(RegistrationType : Object) : Registration {
|
|||
originatingContainer.autowire(instance);
|
||||
}
|
||||
|
||||
this.preDestructor = getPreDestructor(instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private void delegate() getPreDestructor(RegistrationType instance) {
|
||||
void delegate() preDestructor = null;
|
||||
foreach (memberName; __traits(allMembers, RegistrationType)) {
|
||||
static if (__traits(getProtection, __traits(getMember, instance, memberName)) == "public"
|
||||
&& isCallable!(__traits(getMember, instance, memberName))
|
||||
&& hasUDA!(__traits(getMember, instance, memberName), PreDestroy)) {
|
||||
preDestructor = &__traits(getMember, instance, memberName);
|
||||
}
|
||||
}
|
||||
|
||||
return preDestructor;
|
||||
}
|
||||
}
|
||||
|
||||
class AutowireInstantiationContext : InstantiationContext {
|
||||
|
|
|
@ -91,6 +91,15 @@ public enum ResolveOption {
|
|||
*/
|
||||
struct PostConstruct {}
|
||||
|
||||
/**
|
||||
* Methods marked with this UDA within dependencies are called before the container
|
||||
* loses the dependency's registration.
|
||||
*
|
||||
* This method is called when removeRegistration or clearAllRegistrations is called.
|
||||
* It will also be called when the container's destructor is called.
|
||||
*/
|
||||
struct PreDestroy {}
|
||||
|
||||
/**
|
||||
* The dependency container maintains all dependencies registered with it.
|
||||
*
|
||||
|
@ -109,6 +118,10 @@ synchronized class DependencyContainer {
|
|||
private RegistrationOption persistentRegistrationOptions;
|
||||
private ResolveOption persistentResolveOptions;
|
||||
|
||||
~this() {
|
||||
clearAllRegistrations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a dependency by concrete class type.
|
||||
*
|
||||
|
@ -390,6 +403,9 @@ synchronized class DependencyContainer {
|
|||
* Clears all dependency registrations managed by this container.
|
||||
*/
|
||||
public void clearAllRegistrations() {
|
||||
foreach(registrationsOfType; registrations) {
|
||||
callPreDestructorsOfRegistrations(registrationsOfType);
|
||||
}
|
||||
registrations.destroy();
|
||||
}
|
||||
|
||||
|
@ -404,9 +420,20 @@ synchronized class DependencyContainer {
|
|||
* ---
|
||||
*/
|
||||
public void removeRegistration(RegistrationType)() {
|
||||
auto registrationsOfType = *(typeid(RegistrationType) in registrations);
|
||||
callPreDestructorsOfRegistrations(registrationsOfType);
|
||||
registrations.remove(typeid(RegistrationType));
|
||||
}
|
||||
|
||||
private void callPreDestructorsOfRegistrations(shared(Registration[]) registrations) {
|
||||
foreach(registration; registrations) {
|
||||
Registration unsharedRegistration = cast(Registration) registration;
|
||||
if (unsharedRegistration.preDestructor !is null) {
|
||||
unsharedRegistration.preDestructor()();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a global singleton instance of a dependency container.
|
||||
* Deprecated: create new instance with new keyword or implement your own singleton factory (method)
|
||||
|
|
|
@ -22,6 +22,7 @@ class Registration {
|
|||
private Registration linkedRegistration;
|
||||
private shared(DependencyContainer) _originatingContainer;
|
||||
private InstanceFactory _instanceFactory;
|
||||
private void delegate() _preDestructor;
|
||||
|
||||
public @property registeredType() {
|
||||
return _registeredType;
|
||||
|
@ -39,6 +40,14 @@ class Registration {
|
|||
return _instanceFactory;
|
||||
}
|
||||
|
||||
public @property preDestructor() {
|
||||
return _preDestructor;
|
||||
}
|
||||
|
||||
protected @property preDestructor(void delegate() preDestructor) {
|
||||
_preDestructor = preDestructor;
|
||||
}
|
||||
|
||||
this(TypeInfo registeredType, TypeInfo_Class instanceType, InstanceFactory instanceFactory, shared(DependencyContainer) originatingContainer) {
|
||||
this._registeredType = registeredType;
|
||||
this._instanceType = instanceType;
|
||||
|
|
|
@ -95,6 +95,13 @@ version(unittest) {
|
|||
public ComponentA unrelated;
|
||||
}
|
||||
|
||||
class TestInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
assert(key == "values.int");
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Test autowiring concrete type to existing instance
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
|
@ -252,12 +259,6 @@ version(unittest) {
|
|||
// Test autowiring class using value injection
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
class TestInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
assert(key == "values.int");
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, TestInjector);
|
||||
container.register!ComponentA;
|
||||
|
|
|
@ -200,6 +200,21 @@ version(unittest) {
|
|||
}
|
||||
}
|
||||
|
||||
class PreDestroyerOfFates {
|
||||
public bool preDestroyWasCalled = false;
|
||||
|
||||
@PreDestroy
|
||||
public void callMeMaybe() {
|
||||
preDestroyWasCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
int get(string key) {
|
||||
return 8783;
|
||||
}
|
||||
}
|
||||
|
||||
// Test register concrete type
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
|
@ -704,16 +719,28 @@ version(unittest) {
|
|||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Test PreDestroy is called when removing a registration
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!PreDestroyerOfFates;
|
||||
auto instance = container.resolve!PreDestroyerOfFates;
|
||||
container.removeRegistration!PreDestroyerOfFates;
|
||||
assert(instance.preDestroyWasCalled == true);
|
||||
}
|
||||
|
||||
// Test PreDestroy is called when removing all registrations
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!PreDestroyerOfFates;
|
||||
auto instance = container.resolve!PreDestroyerOfFates;
|
||||
container.clearAllRegistrations();
|
||||
assert(instance.preDestroyWasCalled == true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,32 +37,99 @@ version(unittest) {
|
|||
int nums;
|
||||
}
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
assert(key == "conf.stuffs");
|
||||
return 364;
|
||||
}
|
||||
}
|
||||
|
||||
class StringInjector : ValueInjector!string {
|
||||
public override string get(string key) {
|
||||
assert(key == "conf.name");
|
||||
return "Le Chef";
|
||||
}
|
||||
}
|
||||
|
||||
class ThingInjector : ValueInjector!Thing {
|
||||
public override Thing get(string key) {
|
||||
assert(key == "conf.thing");
|
||||
return Thing(8899);
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultIntInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
throw new ValueNotAvailableException(key);
|
||||
}
|
||||
}
|
||||
|
||||
class MandatoryAvailableIntInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
return 7466;
|
||||
}
|
||||
}
|
||||
|
||||
class MandatoryUnavailableIntInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
throw new ValueNotAvailableException(key);
|
||||
}
|
||||
}
|
||||
|
||||
class DependencyInjectedIntInjector : ValueInjector!int {
|
||||
@Autowire
|
||||
public Dependency dependency;
|
||||
|
||||
public override int get(string key) {
|
||||
return 2345;
|
||||
}
|
||||
}
|
||||
|
||||
class CircularIntInjector : ValueInjector!int {
|
||||
@Autowire
|
||||
public ValueInjector!int dependency;
|
||||
|
||||
private int count = 0;
|
||||
|
||||
public override int get(string key) {
|
||||
count += 1;
|
||||
if (count >= 3) {
|
||||
return count;
|
||||
}
|
||||
return dependency.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
class ValueInjectedIntInjector : ValueInjector!int {
|
||||
@Value("five")
|
||||
public int count = 0;
|
||||
|
||||
public override int get(string key) {
|
||||
if (key == "five") {
|
||||
return 5;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
class DependencyValueInjectedIntInjector : ValueInjector!int {
|
||||
@Autowire
|
||||
public ConfigWithDefaults config;
|
||||
|
||||
public override int get(string key) {
|
||||
if (key == "conf.missing") {
|
||||
return 8899;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Test injection of values
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!MyConfig;
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
assert(key == "conf.stuffs");
|
||||
return 364;
|
||||
}
|
||||
}
|
||||
|
||||
class StringInjector : ValueInjector!string {
|
||||
public override string get(string key) {
|
||||
assert(key == "conf.name");
|
||||
return "Le Chef";
|
||||
}
|
||||
}
|
||||
|
||||
class ThingInjector : ValueInjector!Thing {
|
||||
public override Thing get(string key) {
|
||||
assert(key == "conf.thing");
|
||||
return Thing(8899);
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
container.register!(ValueInjector!string, StringInjector);
|
||||
container.register!(ValueInjector!Thing, ThingInjector);
|
||||
|
@ -86,14 +153,7 @@ version(unittest) {
|
|||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ConfigWithDefaults;
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
throw new ValueNotAvailableException(key);
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
container.register!(ValueInjector!int, DefaultIntInjector);
|
||||
|
||||
auto instance = container.resolve!ConfigWithDefaults;
|
||||
assert(instance.noms == 9);
|
||||
|
@ -103,14 +163,7 @@ version(unittest) {
|
|||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ConfigWithMandatory;
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
return 7466;
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
container.register!(ValueInjector!int, MandatoryAvailableIntInjector);
|
||||
|
||||
auto instance = container.resolve!ConfigWithMandatory;
|
||||
assert(instance.nums == 7466);
|
||||
|
@ -120,14 +173,7 @@ version(unittest) {
|
|||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ConfigWithMandatory;
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
throw new ValueNotAvailableException(key);
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
container.register!(ValueInjector!int, MandatoryUnavailableIntInjector);
|
||||
|
||||
assertThrown!ResolveException(container.resolve!ConfigWithMandatory);
|
||||
assertThrown!ValueInjectionException(autowire(container, new ConfigWithMandatory()));
|
||||
|
@ -138,19 +184,8 @@ version(unittest) {
|
|||
auto container = new shared DependencyContainer();
|
||||
auto dependency = new Dependency();
|
||||
container.register!Dependency.existingInstance(dependency);
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
|
||||
@Autowire
|
||||
public Dependency dependency;
|
||||
|
||||
public override int get(string key) {
|
||||
return 2345;
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
auto injector = cast(IntInjector) container.resolve!(ValueInjector!int);
|
||||
container.register!(ValueInjector!int, DependencyInjectedIntInjector);
|
||||
auto injector = cast(DependencyInjectedIntInjector) container.resolve!(ValueInjector!int);
|
||||
|
||||
assert(injector.dependency is dependency);
|
||||
}
|
||||
|
@ -158,25 +193,8 @@ version(unittest) {
|
|||
// Test injecting circular dependencies within value injectors
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
|
||||
@Autowire
|
||||
public ValueInjector!int dependency;
|
||||
|
||||
private int count = 0;
|
||||
|
||||
public override int get(string key) {
|
||||
count += 1;
|
||||
if (count >= 3) {
|
||||
return count;
|
||||
}
|
||||
return dependency.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
auto injector = cast(IntInjector) container.resolve!(ValueInjector!int);
|
||||
container.register!(ValueInjector!int, CircularIntInjector);
|
||||
auto injector = cast(CircularIntInjector) container.resolve!(ValueInjector!int);
|
||||
|
||||
assert(injector.dependency is injector);
|
||||
assert(injector.get("whatever") == 3);
|
||||
|
@ -185,23 +203,8 @@ version(unittest) {
|
|||
// Test value injection within value injectors
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
|
||||
@Value("five")
|
||||
public int count = 0;
|
||||
|
||||
public override int get(string key) {
|
||||
if (key == "five") {
|
||||
return 5;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
auto injector = cast(IntInjector) container.resolve!(ValueInjector!int);
|
||||
container.register!(ValueInjector!int, ValueInjectedIntInjector);
|
||||
auto injector = cast(ValueInjectedIntInjector) container.resolve!(ValueInjector!int);
|
||||
|
||||
assert(injector.count == 5);
|
||||
}
|
||||
|
@ -211,22 +214,8 @@ version(unittest) {
|
|||
auto container = new shared DependencyContainer();
|
||||
container.register!ConfigWithDefaults;
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
|
||||
@Autowire
|
||||
public ConfigWithDefaults config;
|
||||
|
||||
public override int get(string key) {
|
||||
if (key == "conf.missing") {
|
||||
return 8899;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
auto injector = cast(IntInjector) container.resolve!(ValueInjector!int);
|
||||
container.register!(ValueInjector!int, DependencyValueInjectedIntInjector);
|
||||
auto injector = cast(DependencyValueInjectedIntInjector) container.resolve!(ValueInjector!int);
|
||||
|
||||
assert(injector.config.noms == 8899);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue