1 /** 2 * Contains the implementation of application context setup. 3 * 4 * Part of the Poodinis Dependency Injection framework. 5 * 6 * Authors: 7 * Mike Bierlee, m.bierlee@lostmoment.com 8 * Copyright: 2014-2023 Mike Bierlee 9 * License: 10 * This software is licensed under the terms of the MIT license. 11 * The full terms of the license can be found in the LICENSE file. 12 */ 13 14 module poodinis.context; 15 16 import poodinis.container : DependencyContainer; 17 import poodinis.registration : Registration, existingInstance; 18 import poodinis.factory : CreatesSingleton, InstanceFactoryParameters; 19 import poodinis.autowire : autowire; 20 21 import std.traits : hasUDA, ReturnType; 22 23 class ApplicationContext { 24 public void registerDependencies(shared(DependencyContainer) container) { 25 } 26 } 27 28 /** 29 * A component annotation is used for specifying which factory methods produce components in 30 * an application context. 31 */ 32 struct Component { 33 } 34 35 /** 36 * This annotation allows you to specify by which super type the component should be registered. This 37 * enables you to use type-qualified alternatives for dependencies. 38 */ 39 struct RegisterByType(Type) { 40 Type type; 41 } 42 43 /** 44 * Components with the prototype registration will be scoped as dependencies which will create 45 * new instances every time they are resolved. The factory method will be called repeatedly. 46 */ 47 struct Prototype { 48 } 49 50 /** 51 * Register dependencies through an application context. 52 * 53 * An application context allows you to fine-tune dependency set-up and instantiation. 54 * It is mostly used for dependencies which come from an external library or when you don't 55 * want to use annotations to set-up dependencies in your classes. 56 */ 57 public void registerContext(Context : ApplicationContext)(shared(DependencyContainer) container) { 58 auto context = new Context(); 59 context.registerDependencies(container); 60 context.registerContextComponents(container); 61 container.register!(ApplicationContext, Context)().existingInstance(context); 62 autowire(container, context); 63 } 64 65 public void registerContextComponents(ApplicationContextType : ApplicationContext)( 66 ApplicationContextType context, shared(DependencyContainer) container) { 67 foreach (memberName; __traits(allMembers, ApplicationContextType)) { 68 foreach (overload; __traits(getOverloads, ApplicationContextType, memberName)) { 69 static if (__traits(getProtection, overload) == "public" && hasUDA!(overload, Component)) { 70 auto factoryMethod = &__traits(getMember, context, memberName); 71 Registration registration = null; 72 auto createsSingleton = CreatesSingleton.yes; 73 74 foreach (attribute; __traits(getAttributes, overload)) { 75 static if (is(attribute == RegisterByType!T, T)) { 76 registration = container.register!(typeof(attribute.type), 77 ReturnType!factoryMethod); 78 } else static if (__traits(isSame, attribute, Prototype)) { 79 createsSingleton = CreatesSingleton.no; 80 } 81 } 82 83 if (registration is null) { 84 registration = container.register!(ReturnType!factoryMethod); 85 } 86 87 registration.instanceFactory.factoryParameters = InstanceFactoryParameters( 88 registration.instanceType, 89 createsSingleton, 90 null, 91 factoryMethod 92 ); 93 } 94 } 95 } 96 }