Understanding Dependency Injection by writing a DI Container - from scratch! (Part 2)
Tags: java, dependencyinjection, spring, jee
Published: 2020-02-11 21:52:40

---
title: Understanding Dependency Injection by writing a DI Container - from scratch! (Part 2)
published: true
description: Dependency Injection is a difficult topic to grasp. Let's break it down into simple pieces by writing a basic, yet fully functional DI container from scratch!
series: DI From Scratch
tags: java, dependency injection, spring, jee
---

This is the second part of my "DI From Scratch" series. In the previous article, we discussed our basic example, and the problems there are with the "manual" approach. Now, we want to automate the wiring of our service network.

# DI Stage 4: Automating the wiring

> Find the source code of this section [on github](https://github.com/MartinHaeusler/blog/tree/master/diFromScratch/src/main/java/org/example/simplecdi/stage4/collectionofclasses)

Let's focus on the `main()` method, and try to find a more automatic way of creating the service network. Certainly, forgetting to call a setter is a real threat here, and the compiler won't even be able to warn you about it. But our services are structurally different, and there is no **uniform** way to access them... or is there? Java Reflection to the rescue!

All we essentially want to provide our setup with is a list of our service classes. From there, the setup should **construct and wire** the service network. In particular, our `main()` method is only really interested in `ServiceA`, it doesn't even need `ServiceB`. Let's rewrite it like so:

```java
    public static void main(String[] args) throws Exception {
        Set<Class<?>> serviceClasses = new HashSet<>();
        serviceClasses.add(ServiceAImpl.class);
        serviceClasses.add(ServiceBImpl.class);

        ServiceA serviceA = createServiceA(serviceClasses);

        // call business logic
        System.out.println(serviceA.jobA());
    }
```

But how can we implement the "magic" `createServiceA` method? Turns out, it's not *that* hard...

```java
    private static ServiceA createServiceA(Set<Class<?>> serviceClasses) throws Exception{
        // step 1: create an instance of each service class
        Set<Object> serviceInstances = new HashSet<>();
        for(Class<?> serviceClass : serviceClasses){
            Constructor<?> constructor = serviceClass.getConstructor();
            constructor.setAccessible(true);
            serviceInstances.add(constructor.newInstance());
        }
        // step 2: wire them together
        for(Object serviceInstance : serviceInstances){
            for(Field field : serviceInstance.getClass().getDeclaredFields()){
                Class<?> fieldType = field.getType();
                field.setAccessible(true);
                // find a suitable matching service instance
                for(Object matchPartner : serviceInstances){
                    if(fieldType.isInstance(matchPartner)){
                        field.set(serviceInstance, matchPartner);
                    }
                }
            }
        }
        // step 3: from all our service instances, find ServiceA
        for(Object serviceInstance : serviceInstances){
            if(serviceInstance instanceof ServiceA){
                return (ServiceA)serviceInstance;
            }
        }
        // we didn't find the requested service instance
        return null;
    }
```

Let's break it down. In **Step 1** we iterate over our classes, and for each class, we attempt to get the **default constructor** (i.e. the constructor with no arguments). Since neither `ServiceAImpl` nor `ServiceBImpl` specifies any constructor (we deleted them when introducing the getters/setters), the Java compiler provides a public default constructor - so that will work fine. Then, we make this constructor **accessible**. That's just defensive programming to make sure that private constructors will work too. Finally, we call `newInstance()` on the constructor to create the instance of the class, and `add` it to our set of instances.

In **Step 2** we want to wire together our individual service instances. To do so, we look at each service object one by one. We retrieve it's Java class via `getClass()`, and ask that class for all of its `declaredFields` (`declared` means that `private` fields will be returned too). Just like for the constructor, we make sure that the field is accessible, and then we check the `Type` of the field. This will provide us with the service class we need to put into the field. All that's left to do is to find a suitable `matchParter`, an object which is of the type specified by the field. Once we find one, we call `field.set(...)` and assign the match partner to the field. Note that the **first parameter** of the `field.set(...)` method is the object which will have its field value changed.

In **Step 3**, the network is already complete; all that's left to do is to find the instance of `ServiceA`. We can simply scan through or instances and check if we found the right one by using `instanceof ServiceA`.


This might be a little daunting, so maybe try to read this once more. Also, you might want to brush up on your knowledge of Java reflection basics if any of that seems weird to you.

So what did we gain?
 - Our services are wired together automatically.
 - We can no longer forget to call a setter (in fact, we don't need them anymore).
 - Our application will fail **on startup** if the wiring fails, not during the business logic.

The primary pain that we need to treat next is the fact that we do not want to repeat this whole procedure every time we want to get a hold of a service; we want to have the ability to access **every** service in the network, not just one.

# DI Stage 5: Encapsulating the Context

> Find the source code of this section [on github](https://github.com/MartinHaeusler/blog/tree/master/diFromScratch/src/main/java/org/example/simplecdi/stage5/context)

The object which is responsible for holding the service network is called the **Dependency Injection Container**, or (in Spring terms) the **Application Context**. I'm going to use the "context" terminology, but the terms are really synonyms. The primary job of the context is to provide a `getServiceInstance(...)` method which accepts a service class as parameter, and returns the (finished and wired) service instance. So here we go:

```java
public class DIContext {

    private final Set<Object> serviceInstances = new HashSet<>();

    public DIContext(Collection<Class<?>> serviceClasses) throws Exception {
        // create an instance of each service class
        for(Class<?> serviceClass : serviceClasses){
            Constructor<?> constructor = serviceClass.getConstructor();
            constructor.setAccessible(true);
            Object serviceInstance = constructor.newInstance();
            this.serviceInstances.add(serviceInstance);
        }
        // wire them together
        for(Object serviceInstance : this.serviceInstances){
            for(Field field : serviceInstance.getClass().getDeclaredFields()){
                Class<?> fieldType = field.getType();
                field.setAccessible(true);
                // find a suitable matching service instance
                for(Object matchPartner : this.serviceInstances){
                    if(fieldType.isInstance(matchPartner)){
                        field.set(serviceInstance, matchPartner);
                    }
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public <T> T getServiceInstance(Class<T> serviceClass){
        for(Object serviceInstance : this.serviceInstances){
            if(serviceClass.isInstance(serviceInstance)){
                return (T)serviceInstance;
            }
        }
        return null;
    }

}
```

As you can see, the code didn't change much from the previous step, except that we now have an object to encapsulate the context (`DIContext`). Internally, it manages a set of `serviceInstances` which is created just like before from a collection of service classes. The **Step 3** from above has moved into its own `getServiceInstance` method, which accepts the class to retrieve as a parameter. Since we cannot use `instanceof` anymore (it requires a hard-coded class, not a dynamic variable value), we have to fall back to `serviceClass.isInstance(...)` to do the same thing.

We can use this class in our new `main()`:

```java
    public static void main(String[] args) throws Exception {
        DIContext context = createContext();
        doBusinessLogic(context);
    }

    private static DIContext createContext() throws Exception {
        Set<Class<?>> serviceClasses = new HashSet<>();
        serviceClasses.add(ServiceAImpl.class);
        serviceClasses.add(ServiceBImpl.class);
        return new DIContext(serviceClasses);
    }

    private static void doBusinessLogic(DIContext context){
        ServiceA serviceA = context.getServiceInstance(ServiceA.class);
        ServiceB serviceB = context.getServiceInstance(ServiceB.class);
        System.out.println(serviceA.jobA());
        System.out.println(serviceB.jobB());
    }
```

As you can see, we can now easily pull out complete service instances from the context by calling `getServiceInstance` as often as we need to, with different input classes. Also note that the services itself can access each other simply by declaring a field of the proper type - they don't even have to know about the `DIContext` object.

There are still some problems though. For example, what if we want to have a field in our services which does **not** refer to another service (say, an `int` field)? We need a way to **tell our algorithm** which fields we want it to set - and which ones to leave alone.


# DI Stage 6: Annotating fields

> Find the source code of this section [on github](https://github.com/MartinHaeusler/blog/tree/master/diFromScratch/src/main/java/org/example/simplecdi/stage6/annotations)

So how can we tell our algorithm which fields it needs to assign? We could introduce some fancy naming scheme and parse the `field.getName()`, but that's a very error prone solution. Instead, we will use an **Annotation**:


```java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {

}
```

`@Target` tells the compiler on which elements we can use this annotation - we want it to be applicable on fields. With `@Retention` we instruct the compiler to keep this annotation until runtime, and not to discard it during compilation.

Let's annotate our fields:

```java
public class ServiceAImpl implements ServiceA {

    @Inject
    private ServiceB serviceB;

    // rest is the same as before

}
```

```java
public class ServiceBImpl implements ServiceB {

    @Inject
    private ServiceA serviceA;

    // rest is the same as before

}
```

An annotation in and on itself does **nothing**. We need to actively **read** the annotation. So let's do it in the constructor of our `DIContext`:

```java
       // wire them together
       for(Object serviceInstance : this.serviceInstances){
           for(Field field : serviceInstance.getClass().getDeclaredFields()){
               // check that the field is annotated
               if(!field.isAnnotationPresent(Inject.class)){
                   // this field is none of our business
                   continue;
               }
               // rest is the same as before

```

Run the `main()` method again; it should work just like before. However, now you are free to add more fields to your services, and the wiring algorithm won't break.


# Closing words

So far, we have created a DI container which is very basic but functional. It relies on us providing it with the collection of service classes. In the next part, we will discuss how we can actually *discover* our service classes.