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-2022 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 { 25 public void registerDependencies(shared(DependencyContainer) container) 26 { 27 } 28 } 29 30 /** 31 * A component annotation is used for specifying which factory methods produce components in 32 * an application context. 33 */ 34 struct Component 35 { 36 } 37 38 /** 39 * This annotation allows you to specify by which super type the component should be registered. This 40 * enables you to use type-qualified alternatives for dependencies. 41 */ 42 struct RegisterByType(Type) 43 { 44 Type type; 45 } 46 47 /** 48 * Components with the prototype registration will be scoped as dependencies which will create 49 * new instances every time they are resolved. The factory method will be called repeatedly. 50 */ 51 struct Prototype 52 { 53 } 54 55 /** 56 * Register dependencies through an application context. 57 * 58 * An application context allows you to fine-tune dependency set-up and instantiation. 59 * It is mostly used for dependencies which come from an external library or when you don't 60 * want to use annotations to set-up dependencies in your classes. 61 */ 62 public void registerContext(Context : ApplicationContext)(shared(DependencyContainer) container) 63 { 64 auto context = new Context(); 65 context.registerDependencies(container); 66 context.registerContextComponents(container); 67 container.register!(ApplicationContext, Context)().existingInstance(context); 68 autowire(container, context); 69 } 70 71 public void registerContextComponents(ApplicationContextType : ApplicationContext)( 72 ApplicationContextType context, shared(DependencyContainer) container) 73 { 74 foreach (member; __traits(allMembers, ApplicationContextType)) 75 { 76 static if (__traits(getProtection, __traits(getMember, context, 77 member)) == "public" && hasUDA!(__traits(getMember, context, member), Component)) 78 { 79 auto factoryMethod = &__traits(getMember, context, member); 80 Registration registration = null; 81 auto createsSingleton = CreatesSingleton.yes; 82 83 foreach (attribute; __traits(getAttributes, __traits(getMember, context, member))) 84 { 85 static if (is(attribute == RegisterByType!T, T)) 86 { 87 registration = container.register!(typeof(attribute.type), 88 ReturnType!factoryMethod); 89 } 90 else static if (__traits(isSame, attribute, Prototype)) 91 { 92 createsSingleton = CreatesSingleton.no; 93 } 94 } 95 96 if (registration is null) 97 { 98 registration = container.register!(ReturnType!factoryMethod); 99 } 100 101 registration.instanceFactory.factoryParameters = InstanceFactoryParameters( 102 registration.instanceType, createsSingleton, null, factoryMethod); 103 } 104 } 105 }