项目作者: Valefant

项目描述 :
An IoC Container created for learning purposes
高级语言: Kotlin
项目地址: git://github.com/Valefant/Addict.git
创建时间: 2019-01-26T09:59:20Z
项目社区:https://github.com/Valefant/Addict

开源协议:MIT License

下载


Addict

This is the name of the Inversion of Control (IoC) container I am developing for learning purposes.
The binding between the interfaces and implementations are done via code.
Injection takes place at the constructor site.

Example

Here is a basic example showing you how to use the api

  1. interface A
  2. interface B
  3. interface Example
  4. class AImpl : A
  5. class BImpl : B
  6. class ExampleImpl constructor(val a: A, val b: B) : Example
  7. fun main() {
  8. val container = AddictContainer().apply {
  9. bind(A::class, AImpl::class)
  10. bind(B::class, BImpl::class)
  11. bind(Example::class, ExampleImpl::class)
  12. }
  13. val example = container.assemble<Example>()
  14. // example is now a valid instantiated object and ready to use
  15. }

For further examples you can have a look at the tests.

Modules

Addict supports modules to provide a separation of the context you are working on.
When instantiating the container a default module is provided.
Changing a module is straightforward

  1. container.changeModule("newModule")

When the module does not exist it will be created automatically.

Binding

Interfaces and implementations are bound programmatically.
The signature of the function to achieve this is the following

  1. fun <I : Any, C> bind(
  2. kInterface: KClass<I>,
  3. kClass: KClass<C>,
  4. properties: Map<String, Any> = emptyMap(),
  5. scope: Scope = Scope.SINGLETON
  6. ) where C : I

The type parameter I refers to the interface and can be of Any type.

The type parameter C refers to the implementing class.

The where statement makes sure that <C> the class implements only<I> the interface.

Scoping

By default the requested instances are Singletons.
If you want to request different instances of the same type
make sure to pass the Scope.NEW_INSTANCE as last parameter to the bind function.

Assemble

The function signature to assemble a dependency from the container environment looks like this

  1. inline fun <reified T : Any> assemble(): T

In Java there is only the possibility to give the Class as parameter to retain the information during the runtime.
The api would therefore be used in this way assemble(Example.class).
The keyword reified is new to Kotlin and allows us to use the api in a C# like way assemble<Example>().

If you prefer the Java way you can use still use the function in this way assemble(Example::class)

Java Properties Source

The property source is read from the resource folder and is applied in this manner

  1. AddictContainer().apply { propertySource("staging.properties") }

Additions

As known from other frameworks I added the support for string interpolation

Here is a small example

  1. # Setting a property with key=value
  2. example.name=John
  3. example.surname=Doe
  4. # Interpolation is done via ${key}
  5. example.greet=Hello ${example.name} ${example.surname}
  6. # example.greet=Hello John Doe

Injecting values

  1. interface Foo
  2. class FooImpl(
  3. val n: Int,
  4. val greet: String? = "Hi",
  5. val pair: Pair<Char, Char>,
  6. val list: List<Int> = listOf(5, 6, 7, 8)
  7. ) : Foo
  8. val props = mapOf(
  9. "n" to 42,
  10. "greet" to properties.getOrElse("example.greet") { "We are doomed!" },
  11. "pair" to Pair('a', 'z')
  12. )
  13. container.bind(Foo::class, FooImpl::class, props)

The properties for the FooImpl are provided by the props map.
Default class values don’t need to be provided but they can be overwritten if you like.

Lifecycle

Addict provides an interface Lifecycle
with functions to execute during the lifetime of an object within the container context.

PostCreationHook

After the container instantiated a class this function will be executed.
Every property of the class is available within this function context.

ToDo

  • Detecting circular dependencies
  • Make property injection and post construct available by code
    • Therefore we are not only depending on annotations.
      Additionally we can then support default values and other types than strings for the property injection.