diff --git a/example/constructorinjection/app.d b/example/constructorinjection/app.d index adb431b..a80a870 100644 --- a/example/constructorinjection/app.d +++ b/example/constructorinjection/app.d @@ -36,9 +36,17 @@ class Calendar { } } +import std.stdio; + class HardwareClock { // Parameterless constructors will halt any further selection of constructors. - this() {} + this() { + writeln("default constructor"); + } + + this(string name) { + writeln(name); + } // As a result, this constructor will not be used when HardwareClock is created. this(Calendar calendar) { @@ -46,7 +54,6 @@ class HardwareClock { } public void doThings() { - import std.stdio; writeln("Things are being done!"); } } @@ -57,7 +64,10 @@ void main() { auto dependencies = new shared DependencyContainer(); dependencies.register!Scheduler; dependencies.register!Calendar; - dependencies.register!HardwareClock; + dependencies.register!HardwareClock( { + writeln("Running the creator"); + return new HardwareClock("clock name"); + }); auto scheduler = dependencies.resolve!Scheduler; scheduler.scheduleJob(); diff --git a/example/postconstructorpredestructor/app.d b/example/postconstructorpredestructor/app.d index fa2fc81..cc10dee 100644 --- a/example/postconstructorpredestructor/app.d +++ b/example/postconstructorpredestructor/app.d @@ -42,8 +42,14 @@ class AClass { public void main() { auto container = new shared DependencyContainer(); - container.register!ADependency; - container.register!AClass; + container.register!(ADependency).onConstructed((Object obj) { + writeln("ADependency constructed"); + }); + + container.register!(AClass).onConstructed((Object obj) { + writeln("AClass constructed"); + }); + auto instance = container.resolve!AClass; // Will cause the post constructor to be called. container.removeRegistration!AClass; // Will cause the pre destructor to be called. diff --git a/source/poodinis/autowire.d b/source/poodinis/autowire.d index 344e514..6f27e59 100644 --- a/source/poodinis/autowire.d +++ b/source/poodinis/autowire.d @@ -267,9 +267,10 @@ class AutowiredRegistration(RegistrationType : Object) : Registration { void delegate() preDestructor = null; foreach (memberName; __traits(allMembers, RegistrationType)) { mixin(createImportsString!RegistrationType); + enum QualifiedName = fullyQualifiedName!RegistrationType ~ `.` ~ memberName; static if (__traits(compiles, __traits(getProtection, __traits(getMember, instance, memberName))) && __traits(getProtection, __traits(getMember, instance, memberName)) == "public" - && isFunction!(mixin(fullyQualifiedName!RegistrationType ~ `.` ~ memberName)) + && isFunction1!(mixin(QualifiedName)) && hasUDA!(__traits(getMember, instance, memberName), PreDestroy)) { preDestructor = &__traits(getMember, instance, memberName); } diff --git a/source/poodinis/container.d b/source/poodinis/container.d index 85b859b..bf88ec0 100644 --- a/source/poodinis/container.d +++ b/source/poodinis/container.d @@ -30,6 +30,8 @@ debug { import std.stdio; } +alias InjectionInitializer(T) = T delegate(); + /** * Exception thrown when errors occur while resolving a type in a dependency container. */ @@ -147,6 +149,11 @@ synchronized class DependencyContainer { return register!(ConcreteType, ConcreteType)(options); } + Registration register(ConcreteType)(InjectionInitializer!ConcreteType creator, + RegistrationOption options = RegistrationOption.none) { + return register!(ConcreteType, ConcreteType)(creator, options); + } + /** * Register a dependency by super type. * @@ -164,7 +171,9 @@ synchronized class DependencyContainer { * * See_Also: singleInstance, newInstance, existingInstance, RegistrationOption */ - public Registration register(SuperType, ConcreteType : SuperType)(RegistrationOption options = RegistrationOption.none) if (!is(ConcreteType == struct)) { + public Registration register(SuperType, ConcreteType : SuperType)( + RegistrationOption options = RegistrationOption.none) if (!is(ConcreteType == struct)) { + TypeInfo registeredType = typeid(SuperType); TypeInfo_Class concreteType = typeid(ConcreteType); @@ -192,6 +201,44 @@ synchronized class DependencyContainer { return newRegistration; } + /** + * + */ + Registration register(SuperType, ConcreteType : SuperType)(InjectionInitializer!ConcreteType creator, + RegistrationOption options = RegistrationOption.none) if (!is(ConcreteType == struct)) { + + TypeInfo registeredType = typeid(SuperType); + TypeInfo_Class concreteType = typeid(ConcreteType); + + debug(poodinisVerbose) { + writeln(format("DEBUG: Register type %s (as %s)", concreteType.toString(), registeredType.toString())); + } + + auto existingRegistration = getExistingRegistration(registeredType, concreteType); + if (existingRegistration) { + return existingRegistration; + } + + InstanceFactory instanceFactory = new class InstanceFactory { + protected override Object createInstance() { + return creator(); + } + }; + + auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, instanceFactory, this); + newRegistration.singleInstance(); + + static if (!is(SuperType == ConcreteType)) { + if (!hasOption(options, persistentRegistrationOptions, RegistrationOption.doNotAddConcreteTypeRegistration)) { + auto concreteTypeRegistration = register!ConcreteType; + concreteTypeRegistration.linkTo(newRegistration); + } + } + + registrations[registeredType] ~= cast(shared(Registration)) newRegistration; + return newRegistration; + } + private bool hasOption(OptionType)(OptionType options, OptionType persistentOptions, OptionType option) { return ((options | persistentOptions) & option) != 0; } @@ -326,6 +373,12 @@ synchronized class DependencyContainer { } } + bool isRegistered(RegistrationType)() { + TypeInfo typeInfo = typeid(RegistrationType); + auto candidates = typeInfo in registrations; + return candidates !is null; + } + private QualifierType resolveAutowiredInstance(QualifierType)(Registration registration) { QualifierType instance; if (!(cast(Registration[]) autowireStack).canFind(registration)) { @@ -393,10 +446,10 @@ synchronized class DependencyContainer { private void callPostConstructors(Type)(Type instance) { foreach (memberName; __traits(allMembers, Type)) { mixin(createImportsString!Type); - + enum QualifiedName = fullyQualifiedName!Type ~ `.` ~ memberName; static if (__traits(compiles, __traits(getProtection, __traits(getMember, instance, memberName))) && __traits(getProtection, __traits(getMember, instance, memberName)) == "public" - && isFunction!(mixin(fullyQualifiedName!Type ~ `.` ~ memberName)) + && isFunction1!(mixin(QualifiedName)) && hasUDA!(__traits(getMember, instance, memberName), PostConstruct)) { __traits(getMember, instance, memberName)(); } diff --git a/source/poodinis/factory.d b/source/poodinis/factory.d index 24a55af..26e41d2 100644 --- a/source/poodinis/factory.d +++ b/source/poodinis/factory.d @@ -22,6 +22,7 @@ import std.stdio; alias CreatesSingleton = Flag!"CreatesSingleton"; alias InstanceFactoryMethod = Object delegate(); +alias InstanceEventHandler = void delegate(Object instance); class InstanceCreationException : Exception { this(string message, string file = __FILE__, size_t line = __LINE__) { @@ -39,6 +40,7 @@ struct InstanceFactoryParameters { class InstanceFactory { private Object instance = null; private InstanceFactoryParameters _factoryParameters; + private InstanceEventHandler _constructionHandler; this() { factoryParameters = InstanceFactoryParameters(); @@ -75,9 +77,17 @@ class InstanceFactory { } instance = _factoryParameters.factoryMethod(); + if(_constructionHandler !is null) { + _constructionHandler(instance); + } + return instance; } + void onConstructed(InstanceEventHandler handler) { + _constructionHandler = handler; + } + private void printDebugUseExistingInstance() { if (_factoryParameters.instanceType !is null) { writeln(format("DEBUG: Existing instance returned of type %s", _factoryParameters.instanceType.toString())); diff --git a/source/poodinis/polyfill.d b/source/poodinis/polyfill.d index a694374..db87665 100644 --- a/source/poodinis/polyfill.d +++ b/source/poodinis/polyfill.d @@ -43,21 +43,24 @@ static if (!__traits(compiles, basicExceptionCtors)) { } } -static if (!__traits(compiles, isFunction)) { - template isFunction(X...) if (X.length == 1) +static if (!__traits(compiles, isFunction1)) { + template isFunction1(X...) { + static if (X.length > 1) { + enum isFunction1 = false; + } else static if (is(typeof(&X[0]) U : U*) && is(U == function) || is(typeof(&X[0]) U == delegate)) { // x is a (nested) function symbol. - enum isFunction = true; + enum isFunction1 = true; } else static if (is(X[0] T)) { // x is a type. Take the type of it and examine. - enum isFunction = is(T == function); + enum isFunction1 = is(T == function); } else - enum isFunction = false; + enum isFunction1 = false; } } diff --git a/source/poodinis/registration.d b/source/poodinis/registration.d index 0a7dde3..ff73a58 100644 --- a/source/poodinis/registration.d +++ b/source/poodinis/registration.d @@ -60,7 +60,6 @@ class Registration { return linkedRegistration.getInstance(context); } - if (instanceFactory is null) { throw new InstanceCreationException("No instance factory defined for registration of type " ~ registeredType.toString()); } @@ -72,6 +71,12 @@ class Registration { this.linkedRegistration = registration; return this; } + + Registration onConstructed(InstanceEventHandler handler) { + if(instanceFactory !is null) + instanceFactory.onConstructed(handler); + return this; + } } /**