ActiveInject. Fast and Lightweight Dependency Injection Library
Tags: java, news, dependencyinjection, framework
Published: 2020-10-19 14:59:19

## What is ActiveInject?
[**ActiveInject**](https://inject.activej.io) is a lightning-fast and powerful dependency injection library. It has a lot of tools and features to offer: support for nested scopes, singletons and transient bindings, modules, multi-threaded and single-threaded injectors.

At the same time it’s thoroughly optimized with all the dependencies graph preprocessing performed at startup time. According to the benchmarks, in some scenarios **ActiveInject** is 5.5 times faster than Guice and hundreds of times faster than Spring DI. You can check the benchmark sources [here](https://github.com/activej/activej/tree/v2.2/benchmarks/inject/src/main/java/io/activej/inject).

_**ActiveInject** is an independent technology of [**ActiveJ**](https://activej.io) platform. It has no third-party dependencies on its own and can be used as a stand-alone DI library._

## Getting started
Let’s try the library out and bake some virtual cookies using **ActiveInject**. A cookie requires the following ingredients: `Flour`, `Sugar` and `Butter`. These ingredients form a `Pastry` which can be baked into a `Cookie`. Assume each of these entities has a POJO. Let’s start with a basic example:

```java
public void provideAnnotation() {
  Module cookbook = new AbstractModule() {
     @Provides
     Sugar sugar() { return new Sugar("WhiteSugar", 10.f); }

     @Provides
     Butter butter() { return new Butter("PerfectButter", 20.0f); }

     @Provides
     Flour flour() { return new Flour("GoodFlour", 100.0f); }

     @Provides
     Pastry pastry(Sugar sugar, Butter butter, Flour flour) {
        return new Pastry(sugar, butter, flour);
     }

     @Provides
     Cookie cookie(Pastry pastry) {
        return new Cookie(pastry);
     }
  };

  Injector injector = Injector.of(cookbook);
  injector.getInstance(Cookie.class).getPastry().getButter().getName());
}
```

Here we’ve created an [`AbstractModule`](https://github.com/activej/activej/blob/v2.2/core-inject/src/main/java/io/activej/inject/module/AbstractModule.java) named `cookbook` that contains all the required bindings, or “recipes”, for the ingredients. Call `Injector.getInstance` method to get an instance of the `Cookie`.

How does it work from the inside? [`Injector`](https://github.com/activej/activej/blob/v2.2/core-inject/src/main/java/io/activej/inject/Injector.java) provides all the required dependencies for the component recursively traversing the dependencies graph in a postorder way. So it first created `Sugar`, `Butter` and `Flour`, the next was `Pastry`, and finally a `Cookie`.

## Named bindings
But what if you need some special cookie recipes for different people? For example, if you need a sugar-free `Cookie` in addition to a regular one. In this case, you can use `@Named` annotation:

```java
public void namedAnnotationSnippet() {
  Module cookbook = new AbstractModule() {
     @Provides
     @Named("zerosugar")
     Sugar sugar() { return new Sugar("SugarFree", 0.f); }

     @Provides
     @Named("normal")
     Sugar sugar2() { return new Sugar("WhiteSugar", 10.f); }

     @Provides
     Butter butter() { return new Butter("PerfectButter", 20.f); }

     @Provides
     Flour flour() { return new Flour("GoodFlour", 100.f); }

     @Provides
     @Named("normal")
     Pastry pastry1(@Named("normal") Sugar sugar, Butter butter, Flour flour) {
        return new Pastry(sugar, butter, flour);
     }

     @Provides
     @Named("zerosugar")
     Pastry pastry2(@Named("zerosugar") Sugar sugar, Butter butter, Flour flour) {
        return new Pastry(sugar, butter, flour);
     }

     @Provides
     @Named("normal")
     Cookie cookie1(@Named("normal") Pastry pastry) {
        return new Cookie(pastry);
     }

     @Provides
     @Named("zerosugar")
     Cookie cookie2(@Named("zerosugar") Pastry pastry) { return new Cookie(pastry); }
  };
}
```

The usage of the `@Named` annotation is pretty self-illustrative. After annotating bindings you can call either `injector.getInstance(Key.ofName(Cookie.class, "normal"))` or `injector.getInstance(Key.ofName(Cookie.class, "zerosugar"))` and get different instances of the `Cookie` class.

## Non-singleton instances
One of the important **ActiveInject** features is that all the created instances are **singleton by default**. If you call `injector.getInstance(Cookie.class)` several times, each time you’ll get the very same instance from cache. You can easily check it with the following code:

```java
AbstractModule cookbook = new AbstractModule() {
	@Provides
	Integer giveMe() {
		return random.nextInt(1000);
	}
};
Injector injector = Injector.of(cookbook);
Integer firstInt = injector.getInstance(Integer.class);
Integer secondInt = injector.getInstance(Integer.class);
System.out.println("First : " + firstInt + ", second  : " + secondInt);
```

This is a useful optimization, but what if we need non-singleton cookies? In this case, you can use scopes.

[`Scope`](https://github.com/activej/activej/tree/v2.2/core-inject/src/main/java/io/activej/inject/Scope.java) creates “local singletons” which live as long as the scope itself. **ActiveInject** scopes are a bit different from other DI libraries. The internal structure of the `Injector` is a [prefix tree](https://en.wikipedia.org/wiki/Trie) and the prefix is a scope. If you create an `Injector` that is set to a particular scope, it means that `Injector` enters this scope. This can be done several times, so that there are multiple injectors in a single scope.

Let’s create a scope for our cookies. The identifiers of the tree are annotations. So you can simply create your custom scope annotations:

```java
@ScopeAnnotation(threadsafe = false)
@Target({ElementType.METHOD})
@Retention(RUNTIME)
public @interface OrderScope {
}
```

Next, instantiate your scope: 

```java
public static final Scope ORDER_SCOPE = Scope.of(OrderScope.class);
```

Add `@OrderScope` annotation to the instances that are needed as non-singletons (`Cookie`, `Pastry`, `Sugar`, `Flour`, `Butter`) in the following way:

```java
Module cookbook = new AbstractModule() {
  @Provides
 @OrderScope
  Sugar sugar() { return new Sugar("WhiteSugar", 10.f); }

…

  @Provides
  Kitchen kitchen() {return new Kitchen();}
};
```

Now each time we need a new instance of `Сookie`, our `Injector` will create a subinjector to enter the order scope and create a new instance of `Cookie`, while the `Kitchen` will always stay singleton in the root scope.
![ActiveInject scopes](https://dev-to-uploads.s3.amazonaws.com/i/qat3zqyshk46nsii8x0b.png)

## Using ActiveInject with ActiveSpecializer
One more interesting **ActiveInject** feature is that it is fully compatible with another **ActiveJ** library named [**ActiveSpecializer**](https://specializer.activej.io).

**ActiveSpecializer** is a unique technology that relies on a ground-breaking concept of using runtime information about instances of the classes. **ActiveSpecializer** rewrites your code directly in runtime, using runtime information that is contained in the instances of your classes. To be more precise, it transforms all class fields into static class fields, de-virtualizes all the virtual methods calls and replaces them with static method calls. **ActiveSpecializer** shows its best with AST-like structures, so it is very efficient with **ActiveInject**. According to the benchmarks, **ActiveSpecializer** can speed up your code to up to 7 times in some use cases.

To apply **ActiveSpecializer** to your `Injector` simply call `Injector.useSpecializer` method before `Injector` instantiation.

## Summary
**ActiveInject** goes far beyond these basic examples and is capable of fine tuning to match all your needs. You can find more examples on the official **ActiveInject** [website](https://inject.activej.io).

Since **ActiveInject** is a part of **ActiveJ**, it is perfectly compatible with all the platform’s components: HTTP servers and servlets, RPC implementation, bytecode manipulation tools, abstractions for efficient management of distributed file storage and others.