From 2dcb4cdbd0a86c284c045757da4fe62e5f92c413 Mon Sep 17 00:00:00 2001 From: Mike Bierlee Date: Tue, 7 Mar 2023 02:43:03 +0300 Subject: [PATCH] Autoformat a few files --- .vscode/tasks.json | 2 +- CHANGES.md | 201 ++++++++++++++++++++++---------------- README.md | 83 ++++++++-------- TUTORIAL.md | 129 ++++++++++++++++-------- dub.json | 238 ++++++++++++++++++--------------------------- example/README.md | 3 + 6 files changed, 350 insertions(+), 306 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1c5c698..5612650 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -24,4 +24,4 @@ } } ] -} \ No newline at end of file +} diff --git a/CHANGES.md b/CHANGES.md index 854f22b..ca30aa6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,150 +1,179 @@ -Poodinis Changelog -================== +# Poodinis Changelog **Version 8.1.4** (??????) -* FIX some dlang deprecation warnings (#43). Some remain due to further issues in dlang/phobos. + +- FIX some dlang deprecation warnings (#43). Some remain due to further issues in dlang/phobos. **Version 8.1.3** (27-10-2022) -* FIX unnecessary re-registration of types when registerBeforeResolving is specified. -* FIX registerBeforeResolving not working for classes that have no default constructor + +- FIX unnecessary re-registration of types when registerBeforeResolving is specified. +- FIX registerBeforeResolving not working for classes that have no default constructor **Version 8.1.2** (18-02-2022) -* FIX compilation error on importing template types that are not actually types. + +- FIX compilation error on importing template types that are not actually types. **Version 8.1.1** (24-08-2021) -* FIX issues where chained registration scopes get rid of initializedBy's and initializedOnceBy's factory method, reverting it to a default instance factory. + +- FIX issues where chained registration scopes get rid of initializedBy's and initializedOnceBy's factory method, reverting it to a default instance factory. **Version 8.1.0** (06-06-2021) -* ADD ability to provide custom instance creator when registering a dependency (PR #28) -* ADD post-instance-construction callback (PR #28) -* FIX inheritance type template in custom instance creator (PR #29) -* CHANGE injection initializers to be defined as a registration scope instead of via Container.register(). See initializedBy(). -* ADD initializedOnceBy() to create singleton instances via injection initializer. -* FIX multiple template arguments not allowed on constructor argument injection (PR #37) + +- ADD ability to provide custom instance creator when registering a dependency (PR #28) +- ADD post-instance-construction callback (PR #28) +- FIX inheritance type template in custom instance creator (PR #29) +- CHANGE injection initializers to be defined as a registration scope instead of via Container.register(). See initializedBy(). +- ADD initializedOnceBy() to create singleton instances via injection initializer. +- FIX multiple template arguments not allowed on constructor argument injection (PR #37) **Version 8.0.3** (11-06-2018) -* FIX struct types being injected into constructors (Fixes issue #25) + +- FIX struct types being injected into constructors (Fixes issue #25) **Version 8.0.2** (15-04-2018) -* FIX resolving types which use template types with circular type arguments (Thanks to aruthane for fixing this.) + +- FIX resolving types which use template types with circular type arguments (Thanks to aruthane for fixing this.) **Version 8.0.1** (13-08-2017) -* FIX value injectors failing to resolve in certain situations when they inject structs (Fixes issue #20) + +- FIX value injectors failing to resolve in certain situations when they inject structs (Fixes issue #20) **Version 8.0.0** (26-12-2016) -* 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 @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. -* REMOVE previously deprecated getInstance(). + +- 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 @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. +- REMOVE previously deprecated getInstance(). **Version 7.0.1** (05-09-2016) -* FIX codegeneration of constructor injection factories for constructors with dependencies from foreign modules, -such as modules from other libraries (Issue #12). + +- FIX codegeneration of constructor injection factories for constructors with dependencies from foreign modules, + such as modules from other libraries (Issue #12). **Version 7.0.0** (03-09-2016) This version introduces changes which might be incompatible with your current codebase -* ADD constructor injection. Injection is done automatically on resolve. See tutorial and examples for more details. -* REMOVE deprecated registration options. They are still available in properly cased forms. -* REMOVE deprecated register() and resolve() methods which accept variadics and arrays for options. -Since registration and resolve options have become bitfields, you should specify them with logical ANDs. -* DEPRECATE DependencyContainer.getInstance(). To properly make use of inversion of control, one should only -create the dependency container once during set-up and then completly rely on injection. See examples for -proper usage. You can still create your own singleton factory (method) if this is crucial to your design. + +- ADD constructor injection. Injection is done automatically on resolve. See tutorial and examples for more details. +- REMOVE deprecated registration options. They are still available in properly cased forms. +- REMOVE deprecated register() and resolve() methods which accept variadics and arrays for options. + Since registration and resolve options have become bitfields, you should specify them with logical ANDs. +- DEPRECATE DependencyContainer.getInstance(). To properly make use of inversion of control, one should only + create the dependency container once during set-up and then completly rely on injection. See examples for + proper usage. You can still create your own singleton factory (method) if this is crucial to your design. **Version 6.3.0** (27-06-2016) -* CHANGE registration and resolve options to be supplied using bit flags instead. (Thanks to tmccombs) -* DEPRECATE all other forms of supplying registration and resolve options (by array or variadics) + +- CHANGE registration and resolve options to be supplied using bit flags instead. (Thanks to tmccombs) +- DEPRECATE all other forms of supplying registration and resolve options (by array or variadics) **Version 6.2.0** (10-04-2016) -* ADD ability to mark autowire dependencies as optional. When you use UDA @OptionalDependency, a type which fails to autowire will remain null -(or an empty array). No ResolveException is thrown. + +- ADD ability to mark autowire dependencies as optional. When you use UDA @OptionalDependency, a type which fails to autowire will remain null + (or an empty array). No ResolveException is thrown. **Version 6.1.0** (10-02-2016) -* ADD setting persistent registration and resolve options -* DEPRECATE DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION, use doNotAddConcreteTypeRegistration instead -* DEPRECATE supplying register()'s registration options as variadic arguments. Use register(SuperType, ConcreteType)(RegistrationOption[]) instead. -* ADD resolve options to container resolve() -* ADD ability to register a type while resolving it. Use resolve option registerBeforeResolving -* ADD ability to autowire private fields (Thanks to Extrawurst) -* FIX registration of application contexts with non-public members + +- ADD setting persistent registration and resolve options +- DEPRECATE DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION, use doNotAddConcreteTypeRegistration instead +- DEPRECATE supplying register()'s registration options as variadic arguments. Use register(SuperType, ConcreteType)(RegistrationOption[]) instead. +- ADD resolve options to container resolve() +- ADD ability to register a type while resolving it. Use resolve option registerBeforeResolving +- ADD ability to autowire private fields (Thanks to Extrawurst) +- FIX registration of application contexts with non-public members **Version 6.0.0** (29-12-2015) -* CHANGE registration scopes are replaced by a single factory implementation. If you were not doing anything with the internal scope mechanism, you -should not be affected by this change. -* ADD application contexts. You can register dependencies within an application context which allow you to fine-tune the creation of dependency instances. -* CHANGE all public poodinis imports to private. This should not affect you if you use the package import "poodinis" instead of individual modules. -* REMOVE deprecated ADD_CONCRETE_TYPE_REGISTRATION registration option. -* REMOVE deprecated RegistrationOptions alias. + +- CHANGE registration scopes are replaced by a single factory implementation. If you were not doing anything with the internal scope mechanism, you + should not be affected by this change. +- ADD application contexts. You can register dependencies within an application context which allow you to fine-tune the creation of dependency instances. +- CHANGE all public poodinis imports to private. This should not affect you if you use the package import "poodinis" instead of individual modules. +- REMOVE deprecated ADD_CONCRETE_TYPE_REGISTRATION registration option. +- REMOVE deprecated RegistrationOptions alias. **Version 5.0.0** (26-09-2015) -* DEPRECATE ADD_CONCRETE_TYPE_REGISTRATION registration option. It basically does nothing anymore. See next point. -* CHANGE adding registrations by super type always registers them by concrete type as well now. (Previously done with ADD_CONCRETE_TYPE_REGISTRATION). See DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION for the reverse behaviour. -* CHANGE RegistrationOptions enum name to RegistrationOption -* DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead. + +- DEPRECATE ADD_CONCRETE_TYPE_REGISTRATION registration option. It basically does nothing anymore. See next point. +- CHANGE adding registrations by super type always registers them by concrete type as well now. (Previously done with ADD_CONCRETE_TYPE_REGISTRATION). See DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION for the reverse behaviour. +- CHANGE RegistrationOptions enum name to RegistrationOption +- DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead. **Version 4.0.0** (16-08-2015) -* REMOVE deprecated module "dependency.d" + +- REMOVE deprecated module "dependency.d" **Version 3.0.0** (16-08-2015) This version is only compatible with DMD 2.068.0 or higher! -* ADD UDA which always resolved a new instance to an autowired member, regardless of registration scope. + +- ADD UDA which always resolved a new instance to an autowired member, regardless of registration scope. **Version 2.2.0** (07-06-2015) -* ADD canonical package module "package.d". Use "import poodinis;" to import the project. -* DEPRECATE module "dependency.d". Please use the canonical package module. See previous point. -* ADD autowiring of dynamic arrays. All registered instances of the element type of the array will be assigned to it. + +- ADD canonical package module "package.d". Use "import poodinis;" to import the project. +- DEPRECATE module "dependency.d". Please use the canonical package module. See previous point. +- ADD autowiring of dynamic arrays. All registered instances of the element type of the array will be assigned to it. **Version 2.1.0** (14-05-2015) -* ADD option for registering a class by concrete type when registering that class by supertype. + +- ADD option for registering a class by concrete type when registering that class by supertype. **Version 2.0.0** (04-04-2015) This version introduces changes which might be incompatible with your current codebase -* CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible. -The implication is that all dependency container instances must be shared now. -You don't have to change anything if you were only using the singleton dependency container. + +- CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible. + The implication is that all dependency container instances must be shared now. + You don't have to change anything if you were only using the singleton dependency container. **Version 1.0.0** (21-05-2015) This version introduces changes which are incompatible with previous versions -* REMOVE deprecated autowire constructor -* REMOVE deprecated container alias -* ADD documentation for public API -* REMOVE @Autowired UDA. Use @Autowire instead. -* ADD quickstart from readme to compilable example project. -* ADD example project for the use of qualifiers + +- REMOVE deprecated autowire constructor +- REMOVE deprecated container alias +- ADD documentation for public API +- REMOVE @Autowired UDA. Use @Autowire instead. +- ADD quickstart from readme to compilable example project. +- ADD example project for the use of qualifiers **Version 0.3.1** (25-01-2015) -* FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly. + +- FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly. **Version 0.3.0** (24-01-2015) -* ADD alternative workaround to readme for autowire limitation -* CHANGE returning of resolved instances by returning them by qualifier type instead -* ADD debug specifier to reduce verbosity of debug output + +- ADD alternative workaround to readme for autowire limitation +- CHANGE returning of resolved instances by returning them by qualifier type instead +- ADD debug specifier to reduce verbosity of debug output **Version 0.2.0** (14-12-2014) -* ADD ability to register type with multiple concrete types. They can be correctly resolved using qualifiers. -* DEPRECATE template for autowiring in constructor. This workaround is buggy. Use qualifiers instead. + +- ADD ability to register type with multiple concrete types. They can be correctly resolved using qualifiers. +- DEPRECATE template for autowiring in constructor. This workaround is buggy. Use qualifiers instead. **Version 0.1.4** (05-10-2014) -* Make Poodinis compatible with D 2.066.0 and DUB 0.9.22 -* FIX incorrect clearing of registrations -This release should be backwards compatible with the previous versions of D and DUB, but please note that there are no more separate -configurations for release and debug builds. You have to specify a build type in DUB. + +- Make Poodinis compatible with D 2.066.0 and DUB 0.9.22 +- FIX incorrect clearing of registrations + This release should be backwards compatible with the previous versions of D and DUB, but please note that there are no more separate + configurations for release and debug builds. You have to specify a build type in DUB. **Version 0.1.3** (13-07-2014) -* ADD global autowire function for convenience -* CHANGE workaround to be more simple -* FIX autowiring classes which contain non-symbolic declarations such as aliases. As a result, only variables are attempted to be autowired. + +- ADD global autowire function for convenience +- CHANGE workaround to be more simple +- FIX autowiring classes which contain non-symbolic declarations such as aliases. As a result, only variables are attempted to be autowired. **Version 0.1.2** (23-06-2014) -* ADD workaround for failing to autowire types registered by supertype or interface + +- ADD workaround for failing to autowire types registered by supertype or interface **Version 0.1.1** (14-06-2014) -* FIX: Also auto-wire members from base classes + +- FIX: Also auto-wire members from base classes **Version 0.1.0** (04-06-2014) -* Initial open-source release -* ADD support for registering and resolving -* ADD registration scopes -* ADD autowiring + +- Initial open-source release +- ADD support for registering and resolving +- ADD registration scopes +- ADD autowiring diff --git a/README.md b/README.md index 6d527e6..762e11c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -Poodinis Dependency Injection Framework -======================================= +# Poodinis Dependency Injection Framework + Version 8.1.3 Copyright 2014-2023 Mike Bierlee Licensed under the terms of the MIT license - See [LICENSE.txt](LICENSE.txt) @@ -12,26 +12,29 @@ Requires at least a D 2.068.2 compatible compiler Uses the Phobos standard library Can be built with DUB 1.1.1 or higher -Features --------- -* Member injection: Injection of dependencies in class members of any visibility (public, private, etc.) -* Constructor injection: Automatic injection of dependencies in class constructors on creation. -* Value injection: Value-types such as primitives or structs can be injected using custom value injectors. -* Type qualifiers: Inject concrete types into members defined only by abstract types. -* Application contexts: Control the creation of dependencies manually through factory methods. -* Multi-threadable: Dependency containers return the same dependencies across all threads. -* Minimal set-up: Creation and injection of conventional classes requires almost no manual dependency configuration. -* Well-tested: Developed test-driven, a great number of scenarios are tested as part of the test suite. +## Features + +- Member injection: Injection of dependencies in class members of any visibility (public, private, etc.) +- Constructor injection: Automatic injection of dependencies in class constructors on creation. +- Value injection: Value-types such as primitives or structs can be injected using custom value injectors. +- Type qualifiers: Inject concrete types into members defined only by abstract types. +- Application contexts: Control the creation of dependencies manually through factory methods. +- Multi-threadable: Dependency containers return the same dependencies across all threads. +- Minimal set-up: Creation and injection of conventional classes requires almost no manual dependency configuration. +- Well-tested: Developed test-driven, a great number of scenarios are tested as part of the test suite. See the [TUTORIAL.md](TUTORIAL.md) and [examples](example) for a complete walkthrough of all features. -Getting started ---------------- +## Getting started + ### DUB Dependency + See the Poodinis [DUB project page] for instructions on how to include Poodinis into your project. ### Quickstart + The following example shows the typical usage of Poodinis: + ```d import poodinis; @@ -61,49 +64,51 @@ void main() { auto writer = dependencies.resolve!DataWriter; } ``` + Dependency set-up can further be reduced by enabling "Register on resolve". For more details and examples, see the [examples](example) directory. -Documentation -------------- +## Documentation + You can find the public API documentation [here](https://poodinis.dpldocs.info/v8.1.3/index.html). Alternatively you can generate documentation from the source code using DUB: + ``` dub build --build=ddox ``` + The documentation can then be found in docs/ -History -------- +## History + For a full overview of changes, see [CHANGES.md](CHANGES.md) -Value Injectors ---------------- +## Value Injectors + Poodinis doesn't come with implementations of value injectors. Value injectors are available in separate projects: -* [Mirage Config value injector](https://github.com/mbierlee/mirage-injector) -* [Proper-d value injector](https://github.com/mbierlee/poodinis-proper-d-injector) + +- [Mirage Config value injector](https://github.com/mbierlee/mirage-injector) +- [Proper-d value injector](https://github.com/mbierlee/poodinis-proper-d-injector) Have you made any or do you know of any? Please add them to this section via a pull request or open an issue. -Projects Using Poodinis ------------------------ -* [Hunt Framework](https://github.com/huntlabs/hunt-framework): A Web framework for D Programming Language. Full-stack high-performance. -* [Eloquent](https://github.com/SingingBush/eloquent): A lightweight web application written in D -* [ioc](https://github.com/FilipMalczak/ioc): Slow approach to Inversion of Control in D2 language +## Projects Using Poodinis -Future Work ------------ -* Component scan (auto-registration) -* Phobos collections autowiring -* Named qualifiers +- [Hunt Framework](https://github.com/huntlabs/hunt-framework): A Web framework for D Programming Language. Full-stack high-performance. +- [Eloquent](https://github.com/SingingBush/eloquent): A lightweight web application written in D +- [ioc](https://github.com/FilipMalczak/ioc): Slow approach to Inversion of Control in D2 language + +## Future Work + +- Component scan (auto-registration) +- Phobos collections autowiring +- Named qualifiers + +## Contributing -Contributing ------------- Any and all pull requests are welcome! If you (only) want discuss changes before making them, feel free to open an Issue on github. Please develop your changes on (a branch based on) the develop branch. Continuous integration is preferred so feature branches are not neccessary. -[Spring Framework]: http://projects.spring.io/spring-framework/ -[Hypodermic]: https://github.com/ybainier/hypodermic/ -[DUB]: http://code.dlang.org/ -[DUB project page]: http://code.dlang.org/packages/poodinis -[Github issue tracker]: https://github.com/mbierlee/poodinis/issues +[spring framework]: http://projects.spring.io/spring-framework/ +[hypodermic]: https://github.com/ybainier/hypodermic/ +[dub project page]: http://code.dlang.org/packages/poodinis diff --git a/TUTORIAL.md b/TUTORIAL.md index a102c90..f245759 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -1,36 +1,45 @@ -Poodinis Tutorial -================= +# Poodinis Tutorial + This tutorial will give you an overview of all functionality offered by Poodinis and how to use them. -The Container -------------- +## The Container + To register a class, a new dependency container must be instantiated: + ```d // Create a shared container auto dependencies = new shared DependencyContainer(); ``` + A shared dependency container is thread-safe and resolves the same dependencies across all threads. + ### Registering Dependencies + To make dependencies available, they have to be registered: + ```d // Register concrete class dependencies.register!ExampleClass; // Register by super type dependencies.register!(ExampleInterface, ExampleClass); ``` + In the above example, dependencies on the concrete class and interface will resolve an instance of class `ExampleClass`. A dependency registered by super type will automatically be registered by concrete type. If you want to prevent registrations from being both registered by interface and concrete type, use the `doNotAddConcreteTypeRegistration` option when registering: + ```d dependencies.register!(ExampleInterface, ExampleClass)([RegistrationOption.doNotAddConcreteTypeRegistration]); ``` -Resolving Dependencies ----------------------- +## Resolving Dependencies + To manually resolve a dependency, all you have to do is resolve the dependency's type using the container in which it is registered: + ```d auto exampleClassInstance = dependencies.resolve!ExampleClass; ``` + If the class is registered by interface and not by concrete type, you can still resolve the class by concrete type (unless `doNotAddConcreteTypeRegistration` is applied): ```d @@ -40,43 +49,52 @@ assert(exampleClassInstance is exampleClassInstance2); ``` It is also possible to register a type while resolving it. Doing so means you don't need to explicitly register it beforehand. To do this, use the resolve option `registerBeforeResolving`: + ```d dependencies.resolve!ExampleClass([ResolveOption.registerBeforeResolving]); ``` + Naturally this can only be done when you are resolving a concrete type or an interface type by qualifier. -Dependency Creation Behaviour ------------------ +## Dependency Creation Behaviour + You can control how a dependency is resolved by specifying a creation scope during registration. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available: -* Resolve a dependency using a single instance (default): +- Resolve a dependency using a single instance (default): + ```d dependencies.register!ExampleClass.singleInstance(); ``` -* Resolve a dependency with a new instance each time it is resolved: +- Resolve a dependency with a new instance each time it is resolved: + ```d dependencies.register!ExampleClass.newInstance(); ``` -* Resolve a dependency using a pre-existing instance: +- Resolve a dependency using a pre-existing instance: + ```d auto preExistingInstance = new ExampleClass(); dependencies.register!ExampleClass.existingInstance(preExistingInstance); ``` -* Resolve a dependency using a custom initializer delegate: +- Resolve a dependency using a custom initializer delegate: + ```d dependencies.register!ExampleClass.initializedBy({ return new ExampleClass(); }); ``` -Automatic Injection ----------- +## Automatic Injection + The real value of any dependency injection framework comes from its ability to automatically inject dependencies. Poodinis supports automatic injection either through autowiring members annotated with the `@Autowire` UDA or through constructor injection. + ### UDA-based Autowiring + UDA-based autowiring can be achieved by annotating members of a class with the `@Autowire` UDA: + ```d class ExampleClassA {} @@ -90,20 +108,24 @@ auto exampleInstance = new ExampleClassB(); dependencies.autowire(exampleInstance); assert(exampleInstance.dependency !is null); ``` + It is possible to autowire public as well as protected and private members. Dependencies are automatically autowired when a class is resolved. So when you resolve `ExampleClassB`, its member `dependency` is automatically autowired: + ```d dependencies.register!ExampleClassA; dependencies.register!ExampleClassB; auto instance = dependencies.resolve!ExampleClassB; assert(instance.dependency !is null); ``` + If an interface is to be autowired, you must register a concrete class by interface. A class registered only by concrete type can only be injected into members of that type, not its supertypes. Using the UDA `OptionalDependency` you can mark an autowired member as being optional. When a member is optional, no ResolveException will be thrown when the type of the member is not registered and `ResolveOption.registerBeforeResolving` is not set on the container. The member will remain null or an empty array in case of array dependencies. + ```d class ExampleClass { @Autowire @@ -113,7 +135,9 @@ class ExampleClass { ``` ### Constructor Injection + Poodinis also supports automatic injection of dependencies through constructors: + ```d class ExampleClassA {} @@ -129,27 +153,33 @@ dependencies.register!ExampleClassA; dependencies.register!ExampleClassB; auto instance = dependencies.resolve!ExampleClassB; - + ``` -`ExampleClassA` is automatically resolved and passed to `ExampleClassB`'s constructor. + +`ExampleClassA` is automatically resolved and passed to `ExampleClassB`'s constructor. Classes with multiple constructors can be injected. The following rules apply to constructor injection: -* Injection is attempted at the order of declaration. However, this is compiler dependant and may not always be the case. -* Injection is attempted for the first constructor which has non-builtin types only in its parameter list. -* When a constructor with an empty parameter list is found, no other constructors are attempted (and nothing is injected). This can be used to explicitly prevent constructor injection. -* When no injectable constructor is found an InstanceCreationException will be thrown on resolve. + +- Injection is attempted at the order of declaration. However, this is compiler dependant and may not always be the case. +- Injection is attempted for the first constructor which has non-builtin types only in its parameter list. +- When a constructor with an empty parameter list is found, no other constructors are attempted (and nothing is injected). This can be used to explicitly prevent constructor injection. +- When no injectable constructor is found an InstanceCreationException will be thrown on resolve. If the constructors of a class are not suitable for injection, you could manually configure its creation using Application Contexts (see chapter further down). Constructor injection has the advantage of not having to import Poodinis throughout your application. ### Value Injection + Besides injecting class instances, Poodinis can also inject values: + ```d class ExampleClass { @Value("a.key.for.this.value") private int someNumber = 9; // Assignment is kept when the injector cannot find the value associated with key } ``` + The value will automatically be injected during the autowiring process. In order for Poodinis to be able to inject values, ValueInjectors must be available and registered with the dependency container: + ```d class MyIntInjector : ValueInjector!int { int get(string key) { @@ -159,6 +189,7 @@ class MyIntInjector : ValueInjector!int { dependencies.register!(ValueInjector!int, MyIntInjector); ``` + Each injector injects a value of a specific type. You are required to register value injectors by interface; when injecting values, the autowiring process will try to resolve an injector of the member type, such as `ValueInjector!int`. You can only register one value injector per type, a resolve exception will be thrown otherwise (unless you suppress them via resolve options). Naturally a resolve exception will also be thrown when there is no injector for a certain value type. @@ -169,31 +200,36 @@ Value injectors will also be autowired before being used. Value injectors will e Poodinis doesn't come with any value injector implementations. In the [README.md](README.md) you will find a list of projects which use different libraries as value sources. -Circular Dependencies ---------------------- +## Circular Dependencies + Poodinis can autowire circular dependencies when they are registered with `singleInstance` or `existingInstance` registration scopes. Circular dependencies in registrations with `newInstance` scopes will not be autowired, as this would cause an endless loop. Circular dependencies are only supported when autowiring members through the `@Autowire` UDA; circular dependencies in constructors are not supported and will result in an `InstanceCreationException`. -Registering and Resolving Using Qualifiers ------------------------------------------- +## Registering and Resolving Using Qualifiers + You can register multiple concrete types to a super type. When doing so, you will need to specify a qualifier when resolving that type: + ```d // Color is an interface, Blue and Red are classes implementing that interface dependencies.register!(Color, Blue); dependencies.register!(Color, Red); auto blueInstance = dependencies.resolve!(Color, Blue); ``` + If you want to autowire a type registered to multiple concrete types, specify a qualified type as template argument: + ```d class BluePaint { @Autowire!Blue private Color color; } ``` + If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a `ResolveException` is thrown stating that there are multiple candidates for the type to be resolved. -Autowiring All Registered Instances to an Array ------------------------------------------------ +## Autowiring All Registered Instances to an Array + If you have registered multiple concrete types to a super type, you can autowire them all to an array, in which case you can easily operate on them all: + ```d // Color is an interface, Blue and Red are classes implementing that interface @@ -206,27 +242,31 @@ dependencies.register!(Color, Blue); dependencies.register!(Color, Red); auto mixer = dependencies.resolve!ColorMixer; ``` + Member `mixer.colors` will now contain instances of `Blue` and `Red`. The order in which instances are resolved is not guaranteed to be that of the order in which they were registered. -Application Contexts --------------------- +## Application Contexts + You can fine-tune dependency configuration using application contexts. Application contexts allow you to centralize all dependency configuration as well as define how instances of certain classes should be constructed using factory methods. ### Defining and Using Application Contexts + An application context is defined as follows: + ```d class Context : ApplicationContext { public override void registerDependencies(shared(DependencyContainer) container) { container.register!SomeClass; container.register!(SomeInterface, SomeOtherClass).newInstance(); } - + @Component public SomeLibraryClass libraryClass() { return new SomeLibraryClass("This class uses a constructor parameter of a built-in type so I have to register it through an application context"); } } ``` + In the override `registerDependencies()` you can register all dependencies which do not need complex set-up, just like you would do when directly using the dependency container. This override is optional. You can still register simple dependencies outside of the context (or in another context). @@ -237,41 +277,48 @@ They are annotated with the `@Component` UDA to let the container know that thes Factory methods are useful when you have to deal with dependencies which require constructor arguments or elaborate set-up after instantiation. Application contexts have to be registered with a dependency container. They are registered as follows: + ```d container.registerContext!Context; ``` -All registered dependencies can now be resolved by the same dependency container. Registering a context will also register it as a dependency, meaning you can autowire the application context in other classes. + +All registered dependencies can now be resolved by the same dependency container. Registering a context will also register it as a dependency, meaning you can autowire the application context in other classes. You can register as many types of application contexts as you like. ### Autowiring Application Contexts -Application contexts can make use of autowired dependencies like any other dependency. When registering an application context, all its components are registered first after which the application context is autowired. + +Application contexts can make use of autowired dependencies like any other dependency. When registering an application context, all its components are registered first after which the application context is autowired. This means that after the registration of an application context some dependencies will already be resolved and instantiated. The following example illustrates how autowired members can be used in a context: + ```d class Context : ApplicationContext { @Autowire private SomeClass someClass; - + @Autowire private SomeOtherClass someOtherClass; public override void registerDependencies(shared(DependencyContainer) container) { container.register!SomeClass; } - + @Component public SomeLibraryClass libraryClass() { return new SomeLibraryClass(someClass, someOtherClass); } } ``` + As you can see, autowired dependencies can be used within factory methods. When `SomeLibraryClass` is resolved, it will be created with a resolved instance of `SomeClass` and `SomeOtherClass`. As shown, autowired dependencies can be registered within the same application context, but don't neccesarily have to be. You can even autowire dependencies which are created within a factory method within the same application context. Application contexts are directly autowired after they have been registered. This means that all autowired dependencies which are not registered in the application context itself need to be registered before registering the application context. ### Controlling Component Registration + You can further influence how components are registered and created with additional UDAs: + ```d class Context : ApplicationContext { @Component @@ -283,34 +330,40 @@ class Context : ApplicationContext { } ``` -Persistent Registration and Resolve Options -------------------------------------------- +## Persistent Registration and Resolve Options + If you want registration options to be persistent (applicable for every call to `register()`), you can use the container method `setPersistentRegistrationOptions()`: + ```d dependencies.setPersistentRegistrationOptions(RegistrationOption.doNotAddConcreteTypeRegistration); // Sets the options dependencies.unsetPersistentRegistrationOptions(); // Clears the persistentent options ``` + Likewise, the same is possible for resolve options: + ```d dependencies.setPersistentResolveOptions(ResolveOption.registerBeforeResolving); // Sets the options dependencies.unsetPersistentResolveOptions(); // Clears the persistentent options ``` + Please note that setting options will unset previously set options; the options specified will be the only ones in effect. -Post-Constructors and Pre-Destructors -------------------------------------- +## Post-Constructors and Pre-Destructors + Using the `@PostConstruct` and `@PreDestroy` UDAs you can let the container call public methods in your class right after the container constructed it or when it loses its registration for your class. The pre-constructor is called right after the container has created a new instance of your class and has autowired its members. The post-construtor is called when `removeRegistration` or `clearAllRegistrations` is called, or when the container's destructor is called. A post-constructor or pre-destructor must have the signature `void(void)`. + ```d class MyFineClass { @PostConstruct void postConstructor() { // Is called right after MyFineClass is created and autowired } - + @PreDestroy void preDestructor() { // Is called right before MyFineClass's registration is removed from the container } } ``` + You can have multiple post-constructors and pre-destructors, they will all be called; however, the order in which they are called is undetermined. diff --git a/dub.json b/dub.json index 5acb150..1b467f1 100644 --- a/dub.json +++ b/dub.json @@ -1,144 +1,98 @@ { - "name" : "poodinis", - "description" : "A dependency injection framework with support for autowiring.", - "authors": ["Mike Bierlee"], - "copyright": "Copyright 2014-2023 Mike Bierlee", - "license": "MIT", - "-ddoxTool": "scod", - "-ddoxFilterArgs": [ - - ], - "configurations": [ - { - "name": "library", - "targetType": "library" - }, - { - "name": "unittest", - "targetType": "executable", - "sourcePaths": [ - "test" - ], - "mainSourceFile": "test/poodinis/testmain.d" - }, - { - "name": "unittestVerbose", - "targetType": "executable", - "debugVersions": ["poodinisVerbose"], - "sourcePaths": [ - "test" - ], - "mainSourceFile": "test/poodinis/testmain.d" - }, - { - "name" : "quickstartExample", - "targetType": "executable", - "targetName": "quickstartExample", - "sourcePaths": [ - "example/quickstart" - ], - "importPaths": [ - "source" - ] - }, - { - "name" : "qualifiersExample", - "targetType": "executable", - "targetName": "qualifiersExample", - "sourcePaths": [ - "example/qualifiers" - ], - "importPaths": [ - "source" - ] - }, - { - "name" : "arrayCompletionExample", - "targetType": "executable", - "targetName": "arrayCompletionExample", - "sourcePaths": [ - "example/arraycompletion" - ], - "importPaths": [ - "source" - ] - }, - { - "name" : "annotationsExample", - "targetType": "executable", - "targetName": "annotationsExample", - "sourcePaths": [ - "example/annotations" - ], - "importPaths": [ - "source" - ] - }, - { - "name" : "applicationContextExample", - "targetType": "executable", - "targetName": "applicationContextExample", - "sourcePaths": [ - "example/applicationcontext" - ], - "importPaths": [ - "source" - ] - }, - { - "name" : "registerOnResolveExample", - "targetType": "executable", - "targetName": "registerOnResolveExample", - "sourcePaths": [ - "example/registeronresolve" - ], - "importPaths": [ - "source" - ] - }, - { - "name" : "constructorInjectionExample", - "targetType": "executable", - "targetName": "constructorInjectionExample", - "sourcePaths": [ - "example/constructorinjection" - ], - "importPaths": [ - "source" - ] - }, - { - "name" : "valueInjectionExample", - "targetType": "executable", - "targetName": "valueInjectionExample", - "sourcePaths": [ - "example/valueinjection" - ], - "importPaths": [ - "source" - ] - }, - { - "name" : "postConPreDestExample", - "targetType": "executable", - "targetName": "postConPreDestExample", - "sourcePaths": [ - "example/postconpredest" - ], - "importPaths": [ - "source" - ] - }, - { - "name" : "injectionInitializerExample", - "targetType": "executable", - "targetName": "injectionInitializerExample", - "sourcePaths": [ - "example/injectioninitializer" - ], - "importPaths": [ - "source" - ] - } - ] + "name": "poodinis", + "description": "A dependency injection framework with support for autowiring.", + "authors": ["Mike Bierlee"], + "copyright": "Copyright 2014-2023 Mike Bierlee", + "license": "MIT", + "-ddoxTool": "scod", + "-ddoxFilterArgs": [], + "configurations": [ + { + "name": "library", + "targetType": "library" + }, + { + "name": "unittest", + "targetType": "executable", + "sourcePaths": ["test"], + "mainSourceFile": "test/poodinis/testmain.d" + }, + { + "name": "unittestVerbose", + "targetType": "executable", + "debugVersions": ["poodinisVerbose"], + "sourcePaths": ["test"], + "mainSourceFile": "test/poodinis/testmain.d" + }, + { + "name": "quickstartExample", + "targetType": "executable", + "targetName": "quickstartExample", + "sourcePaths": ["example/quickstart"], + "importPaths": ["source"] + }, + { + "name": "qualifiersExample", + "targetType": "executable", + "targetName": "qualifiersExample", + "sourcePaths": ["example/qualifiers"], + "importPaths": ["source"] + }, + { + "name": "arrayCompletionExample", + "targetType": "executable", + "targetName": "arrayCompletionExample", + "sourcePaths": ["example/arraycompletion"], + "importPaths": ["source"] + }, + { + "name": "annotationsExample", + "targetType": "executable", + "targetName": "annotationsExample", + "sourcePaths": ["example/annotations"], + "importPaths": ["source"] + }, + { + "name": "applicationContextExample", + "targetType": "executable", + "targetName": "applicationContextExample", + "sourcePaths": ["example/applicationcontext"], + "importPaths": ["source"] + }, + { + "name": "registerOnResolveExample", + "targetType": "executable", + "targetName": "registerOnResolveExample", + "sourcePaths": ["example/registeronresolve"], + "importPaths": ["source"] + }, + { + "name": "constructorInjectionExample", + "targetType": "executable", + "targetName": "constructorInjectionExample", + "sourcePaths": ["example/constructorinjection"], + "importPaths": ["source"] + }, + { + "name": "valueInjectionExample", + "targetType": "executable", + "targetName": "valueInjectionExample", + "sourcePaths": ["example/valueinjection"], + "importPaths": ["source"] + }, + { + "name": "postConPreDestExample", + "targetType": "executable", + "targetName": "postConPreDestExample", + "sourcePaths": ["example/postconpredest"], + "importPaths": ["source"] + }, + { + "name": "injectionInitializerExample", + "targetType": "executable", + "targetName": "injectionInitializerExample", + "sourcePaths": ["example/injectioninitializer"], + "importPaths": ["source"] + } + ] } diff --git a/example/README.md b/example/README.md index 59e146f..9335d34 100644 --- a/example/README.md +++ b/example/README.md @@ -1,10 +1,13 @@ To run any of these examples, specify the relevant DUB configuration: + ``` dub run --build=release --config=Example ``` + Where \ corresponds to the name of the folder where the code example is in (camel-cased). e.g: + ``` dub run --build=release --config=constructorInjectionExample ```