1 /** 2 * Contains functionality for autowiring dependencies using a dependency container. 3 * 4 * This module is used in a dependency container for autowiring dependencies when resolving them. 5 * You typically only need this module if you want inject dependencies into a class instance not 6 * managed by a dependency container. 7 * 8 * Part of the Poodinis Dependency Injection framework. 9 * 10 * Authors: 11 * Mike Bierlee, m.bierlee@lostmoment.com 12 * Copyright: 2014-2023 Mike Bierlee 13 * License: 14 * This software is licensed under the terms of the MIT license. 15 * The full terms of the license can be found in the LICENSE file. 16 */ 17 18 module poodinis.autowire; 19 20 import poodinis.container : DependencyContainer, PreDestroy, ResolveException, ResolveOption; 21 import poodinis.registration : Registration, InstantiationContext; 22 import poodinis.factory : InstanceFactory, InstanceFactoryParameters, CreatesSingleton; 23 import poodinis.valueinjection : ValueInjector, ValueInjectionException, 24 ValueNotAvailableException, Value, MandatoryValue; 25 import poodinis.altphobos : isFunction; 26 import poodinis.imports : createImportsString; 27 28 import std.exception : enforce; 29 import std.string : format; 30 import std.traits : BaseClassesTuple, FieldNameTuple, fullyQualifiedName, hasUDA, isDynamicArray; 31 import std.range : ElementType; 32 33 debug { 34 import std.stdio : writeln; 35 } 36 37 private struct UseMemberType { 38 } 39 40 /** 41 * UDA for annotating class members as candidates for autowiring. 42 * 43 * Optionally a template parameter can be supplied to specify the type of a qualified class. The qualified type 44 * of a concrete class is used to autowire members declared by supertype. If no qualifier is supplied, the type 45 * of the member is used as qualifier. 46 * 47 * Note: @Autowire is considered legacy, but not deprecated. Using @Inject is preferred. 48 * 49 * Examples: 50 * Annotate member of class to be autowired: 51 * --- 52 * class Car { 53 * @Autowire 54 * public Engine engine; 55 * } 56 * --- 57 * 58 * Annotate member of class with qualifier: 59 * --- 60 * class FuelEngine : Engine { ... } 61 * class ElectricEngine : Engine { ... } 62 * 63 * class HybridCar { 64 * @Autowire!FuelEngine 65 * public Engine fuelEngine; 66 * 67 * @Autowire!ElectricEngine 68 * public Engine electricEngine; 69 * } 70 * --- 71 * The members of an instance of "HybridCar" will now be autowired properly, because the autowire mechanism will 72 * autowire member "fuelEngine" as if it's of type "FuelEngine". This means that the members of instance "fuelEngine" 73 * will also be autowired because the autowire mechanism knows that member "fuelEngine" is an instance of "FuelEngine" 74 * 75 * See_Also: Inject 76 */ 77 struct Autowire(QualifierType) { 78 QualifierType qualifier; 79 } 80 81 /** 82 * UDA for marking autowired dependencies optional. 83 * Optional dependencies will not lead to a resolveException when there is no type registered for them. 84 * The member will remain null. 85 */ 86 struct OptionalDependency { 87 } 88 89 /** 90 * UDA for annotating class members to be autowired with a new instance regardless of their registration scope. 91 * 92 * Examples: 93 *--- 94 * class Car { 95 * @Autowire 96 * @AssignNewInstance 97 * public Antenna antenna; 98 * } 99 *--- 100 * antenna will always be assigned a new instance of class Antenna. 101 */ 102 struct AssignNewInstance { 103 } 104 105 private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress) { 106 debug { 107 writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress)); 108 } 109 } 110 111 /** 112 * Autowires members of a given instance using dependencies registered in the given container. 113 * 114 * All members of the given instance, which are annotated using the "Autowire" UDA, are autowired. 115 * Members can have any visibility (public, private, etc). All members are resolved using the given 116 * container. Qualifiers are used to determine the type of class to resolve for any member of instance. 117 * 118 * See_Also: Autowire 119 */ 120 public void autowire(Type)(shared(DependencyContainer) container, Type instance) { 121 debug (poodinisVerbose) { 122 printDebugAutowiredInstance(typeid(Type), &instance); 123 } 124 125 // Recurse into base class if there are more between Type and Object in the hierarchy 126 static if (BaseClassesTuple!Type.length > 1) { 127 autowire!(BaseClassesTuple!Type[0])(container, instance); 128 } 129 130 foreach (index, name; FieldNameTuple!Type) { 131 autowireMember!(name, index, Type)(container, instance); 132 } 133 } 134 135 private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType, 136 void* candidateInstanceAddress, TypeInfo instanceType, void* instanceAddress, string member) { 137 debug { 138 writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", candidateInstanceType, 139 candidateInstanceAddress, instanceType, instanceAddress, member)); 140 } 141 } 142 143 private void printDebugAutowiringArray(TypeInfo superTypeInfo, 144 TypeInfo instanceType, void* instanceAddress, string member) { 145 debug { 146 writeln(format("DEBUG: Autowired all registered instances of super type %s to [%s@%s].%s", 147 superTypeInfo, instanceType, instanceAddress, member)); 148 } 149 } 150 151 private void autowireMember(string member, size_t memberIndex, Type)( 152 shared(DependencyContainer) container, Type instance) { 153 foreach (attribute; __traits(getAttributes, Type.tupleof[memberIndex])) { 154 static if (is(attribute == Autowire!T, T)) { 155 injectInstance!(member, memberIndex, typeof(attribute.qualifier))(container, instance); 156 } else static if (__traits(isSame, attribute, Autowire)) { 157 injectInstance!(member, memberIndex, UseMemberType)(container, instance); 158 } else static if (is(typeof(attribute) == Value)) { 159 enum key = attribute.key; 160 injectValue!(member, memberIndex, key, false)(container, instance); 161 } else static if (is(typeof(attribute) == MandatoryValue)) { 162 enum key = attribute.key; 163 injectValue!(member, memberIndex, key, true)(container, instance); 164 } 165 } 166 } 167 168 private void injectInstance(string member, size_t memberIndex, QualifierType, Type)( 169 shared(DependencyContainer) container, Type instance) { 170 if (instance.tupleof[memberIndex] is null) { 171 alias MemberType = typeof(Type.tupleof[memberIndex]); 172 enum isOptional = hasUDA!(Type.tupleof[memberIndex], OptionalDependency); 173 174 static if (isDynamicArray!MemberType) { 175 injectMultipleInstances!(member, memberIndex, isOptional, MemberType)(container, 176 instance); 177 } else { 178 injectSingleInstance!(member, memberIndex, isOptional, MemberType, QualifierType)(container, 179 instance); 180 } 181 } 182 } 183 184 private void injectMultipleInstances(string member, size_t memberIndex, 185 bool isOptional, MemberType, Type)(shared(DependencyContainer) container, Type instance) { 186 alias MemberElementType = ElementType!MemberType; 187 static if (isOptional) { 188 auto instances = container.resolveAll!MemberElementType(ResolveOption.noResolveException); 189 } else { 190 auto instances = container.resolveAll!MemberElementType; 191 } 192 193 instance.tupleof[memberIndex] = instances; 194 debug (poodinisVerbose) { 195 printDebugAutowiringArray(typeid(MemberElementType), typeid(Type), &instance, member); 196 } 197 } 198 199 private void injectSingleInstance(string member, size_t memberIndex, 200 bool isOptional, MemberType, QualifierType, Type)( 201 shared(DependencyContainer) container, Type instance) { 202 debug (poodinisVerbose) { 203 TypeInfo qualifiedInstanceType = typeid(MemberType); 204 } 205 206 enum assignNewInstance = hasUDA!(Type.tupleof[memberIndex], AssignNewInstance); 207 208 MemberType qualifiedInstance; 209 static if (!is(QualifierType == UseMemberType)) { 210 qualifiedInstance = createOrResolveInstance!(MemberType, QualifierType, 211 assignNewInstance, isOptional)(container); 212 debug (poodinisVerbose) { 213 qualifiedInstanceType = typeid(QualifierType); 214 } 215 } else { 216 qualifiedInstance = createOrResolveInstance!(MemberType, MemberType, 217 assignNewInstance, isOptional)(container); 218 } 219 220 instance.tupleof[memberIndex] = qualifiedInstance; 221 222 debug (poodinisVerbose) { 223 printDebugAutowiringCandidate(qualifiedInstanceType, 224 &qualifiedInstance, typeid(Type), &instance, member); 225 } 226 } 227 228 private QualifierType createOrResolveInstance(MemberType, QualifierType, 229 bool createNew, bool isOptional)(shared(DependencyContainer) container) { 230 static if (createNew) { 231 auto instanceFactory = new InstanceFactory(); 232 instanceFactory.factoryParameters = InstanceFactoryParameters(typeid(MemberType), 233 CreatesSingleton.no); 234 return cast(MemberType) instanceFactory.getInstance(); 235 } else { 236 static if (isOptional) { 237 return container.resolve!(MemberType, QualifierType)(ResolveOption.noResolveException); 238 } else { 239 return container.resolve!(MemberType, QualifierType); 240 } 241 } 242 } 243 244 private void injectValue(string member, size_t memberIndex, string key, bool mandatory, Type)( 245 shared(DependencyContainer) container, Type instance) { 246 alias MemberType = typeof(Type.tupleof[memberIndex]); 247 try { 248 auto injector = container.resolve!(ValueInjector!MemberType); 249 instance.tupleof[memberIndex] = injector.get(key); 250 debug (poodinisVerbose) { 251 printDebugValueInjection(typeid(Type), &instance, member, typeid(MemberType), key); 252 } 253 } catch (ResolveException e) { 254 throw new ValueInjectionException(format( 255 "Could not inject value of type %s into %s.%s: value injector is missing for this type.", 256 typeid(MemberType), typeid(Type), member)); 257 } catch (ValueNotAvailableException e) { 258 static if (mandatory) { 259 throw new ValueInjectionException(format("Could not inject value of type %s into %s.%s", 260 typeid(MemberType), typeid(Type), member), e); 261 } 262 } 263 } 264 265 private void printDebugValueInjection(TypeInfo instanceType, 266 void* instanceAddress, string member, TypeInfo valueType, string key) { 267 debug { 268 writeln(format("DEBUG: Injected value with key '%s' of type %s into [%s@%s].%s", 269 key, valueType, instanceType, instanceAddress, member)); 270 } 271 } 272 273 /** 274 * Autowire the given instance using the globally available dependency container. 275 * 276 * See_Also: DependencyContainer 277 * Deprecated: Using the global container is undesired. See DependencyContainer.getInstance(). 278 */ 279 public deprecated void globalAutowire(Type)(Type instance) { 280 DependencyContainer.getInstance().autowire(instance); 281 } 282 283 class AutowiredRegistration(RegistrationType : Object) : Registration { 284 private shared(DependencyContainer) container; 285 286 public this(TypeInfo registeredType, InstanceFactory instanceFactory, 287 shared(DependencyContainer) originatingContainer) { 288 super(registeredType, typeid(RegistrationType), instanceFactory, originatingContainer); 289 } 290 291 public override Object getInstance( 292 InstantiationContext context = new AutowireInstantiationContext()) { 293 enforce(!(originatingContainer is null), 294 "The registration's originating container is null. There is no way to resolve autowire dependencies."); 295 296 RegistrationType instance = cast(RegistrationType) super.getInstance(context); 297 298 AutowireInstantiationContext autowireContext = cast(AutowireInstantiationContext) context; 299 enforce(!(autowireContext is null), "Given instantiation context type could not be cast to an AutowireInstantiationContext. If you relied on using the default assigned context: make sure you're calling getInstance() on an instance of type AutowiredRegistration!"); 300 if (autowireContext.autowireInstance) { 301 originatingContainer.autowire(instance); 302 } 303 304 this.preDestructor = getPreDestructor(instance); 305 306 return instance; 307 } 308 309 private void delegate() getPreDestructor(RegistrationType instance) { 310 void delegate() preDestructor = null; 311 static foreach (memberName; __traits(allMembers, RegistrationType)) { 312 static foreach (overload; __traits(getOverloads, RegistrationType, memberName)) { 313 static if (__traits(compiles, __traits(getProtection, overload)) 314 && __traits(getProtection, overload) == "public" 315 && isFunction!overload 316 && hasUDA!(overload, PreDestroy)) { 317 preDestructor = &__traits(getMember, instance, memberName); 318 } 319 } 320 } 321 322 return preDestructor; 323 } 324 } 325 326 class AutowireInstantiationContext : InstantiationContext { 327 public bool autowireInstance = true; 328 }