Forms of Dependency Injection

Forms of Dependency Injection

The basic idea of the Dependency Injection is to have a separate object, an assembler, that populates a field in the lister class with an appropriate implementation for the finder interface.

What is it?

https://youtu.be/RlfLCWKxHJ0?t=5m52s

Properly using dependency injection with Spring means allowing it to wire all your objects together by scanning all desired configuration classes; this proves to be useful for decoupling relationships and also makes testing a whole lot easier. Avoid tight coupling of classes.

Different ways to implement dependency injection

Spring documentation strictly defines only two types of injection: constructor and setter injection.

Explain the scenarios where you used one type as opposed to the others and the reason why.

There are a few ways that Spring uses to inject dependencies:

1. Constructor Injection — enforcing immutability

This is the most straightforward and recommended way of dependency injection. A dependent class has a constructor, where all dependencies are set, they will be provided by Spring container according to XML, Java or annotation based configurations.

Why is this preferable to the other ways? One way to keep your business logic free from Spring Boot code is to rely on Constructor Injection. Not only is the @Autowired annotation optional on constructors, you also get the benefit of being able to easily instantiate the beans without Spring.

private final InventoryMapper inventoryMapper;

public InventoryController(InventoryMapper inventoryMapper) {
    this.inventoryMapper = inventoryMapper;
}

Another example:

package example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;

@Service
class Service1 {
    void doSmth() {
        System.out.println("Service1");
    }
}

@Service
class Service2 {
    void doSmth() {
        System.out.println("Service2");
    }
}

@Service
class DependentService {
    private final Service1 service1;
    private final Service2 service2;

    @Autowired
    public DependentService(Service1 service1, Service2 service2) {
        this.service1 = service1;
        this.service2 = service2;
    }

    void doSmth() {
        service1.doSmth();
        service2.doSmth();
    }

}

public class Main {
    public static void main(String[] args) {
        ApplicationContext container = new ClassPathXmlApplicationContext("spring.xml");
        ((DependentService) container.getBean("dependentService")).doSmth();
    }
}

One important detail — you can have many more constructors in your class, only one of them should qualify for dependency injection.

Advantages of constructor injection

  1. Constructed object is immutable and returned to the client in a fully initialized state.
  2. An issue with a growing amount of dependencies is immediately visible with this approach. More dependencies bigger the constructor.
  3. Can be combined with setter injection or field injection, constructor parameters indicate required dependencies, others — optional.

And the disadvantages

  1. No possibility to change object’s dependencies later — inflexibility.
  2. Higher chance to have circular dependencies, so-called chicken-and-egg scenario.

Lombok’s @RequiredArgsConstructor annotation

This can be done using Lombok’s @RequiredArgsConstructor annotation

See Project Lombok

2. Setter injection — enjoy the mutability

private InventoryMapper inventoryMapper;

public void setInventoryMapper(InventoryMapper inventoryMapper) {
    this.inventoryMapper = inventoryMapper;
}

Another example:

To show this way of injection, let us use the same code that was in examples before. As there won’t be that many changes from Constructor Injection, for the sake of brevity, let us change only the logical parts.

@Service
class DependentService {
    private Service1 service1;
    private Service2 service2;

    @Autowired
    public void setService1(Service1 service1) {
        this.service1 = service1;
    }

    @Autowired
    public void setService2(Service2 service2) {
        this.service2 = service2;
    }

    void doSmth() {
        service1.doSmth();
        service2.doSmth();
    }
}

Here we’ve just put the @Autowired annotation on setters. Again, @Inject would work here as well without any issues.

As the name of injection type implies, we should create setters for our dependencies first. A constructor is not required in this case.

Setters presence is required for this type of injection in the same way as a constructor is required for constructor injection.

How to combine constructor injection with setter injection?

@Service
class DependentService {
    private final Service1 service1;
    private Service2 service2;

    @Autowired
    public DependentService(Service1 service1) {
        this.service1 = service1;
    }

    @Autowired
    public void setService2(Service2 service2) {
        this.service2 = service2;
    }

    void doSmth() {
        service1.doSmth();
        service2.doSmth();
    }
}

In this example, the service1 object is a mandatory dependency (final property) and is set only once during instantiation of DependentService instance. Meanwhile, service2 is optional, can contain null at first and its value may be changed any time after creation by calling setter.

Advantages

  1. Flexibility in dependency resolution or object reconfiguration, it can be done anytime. Plus, this freedom solves the circular dependency issue of constructor injection.

Disadvantages

  1. Null checks are required, because dependencies may not be set at the moment.
  2. Potentially more error-prone and less secure than constructor injection due to the possibility of overriding dependencies.

3. Fields Injection/Annotation injection - nobody likes it?

This type of injection is possible only in the annotation based approach due to the fact that it is not really a new injection type — under the hood, Spring uses reflection to set these values.

@Autowired
private InventoryMapper inventoryMapper;

Advantages

  1. Easy to use, no constructors or setters required
  2. Can be easily combined with the constructor and/or setter approach

Disadvantages

  1. Less control over object instantiation. In order to instantiate the object of a class for a test, you will need either a Spring container configured or mock library — depends on the test you are writing.
  2. A number of dependencies can reach dozens until you notice that something went wrong in your design.
  3. No immutability — the same as for setter injection.

4. Lookup Method injection — what? why? when?

This injection type is used less frequently than all the others described above because of the special use case, namely the injection of dependency with a smaller lifetime.

By default, all beans in Spring are created as singletons, which means they will be created in a container once and the same object will be injected anywhere it is requested. However, sometimes, a different strategy is required, for example, each method call should be done from a fresh object. And now imagine that this short lifetime object is injected to the singleton object, will Spring refresh this dependency automatically with every invocation? No, the dependency will be still perceived as a singleton unless we indicate the existence of this special dependency type.

Returning back to practice, we have again 3 services, one of them is depending on others, service2 is the usual object which can be injected in DependentService by any of previously described dependency injection techniques, let’s say setter injection. The object of Service1 will be different, it can not be injected once, a new instance should be accessed with each call — let’s create a method that will provide this object and let Spring know about it.

@Service
@Scope(value = "prototype")
class Service1 {
    void doSmth() {
        System.out.println("Service1");
    }
}

@Service
abstract class DependentService {
    private Service2 service2;

    @Autowired
    public void setService2(Service2 service2) {
        this.service2 = service2;
    }

    void doSmth() {
        createService1().doSmth();
        service2.doSmth();
    }

    @Lookup
    protected abstract Service1 createService1();
}

We haven’t declared an object of Service1 as a usual dependency, instead, we’ve specified method which will be overridden by Spring framework in order to return the latest instance of Service1 class.

Method-provider of dependency doesn’t have to be abstract and protected, it can be public and contain implementation but keep in mind that it will be overridden in a subclass, created by Spring.

We will have to indicate first that service1 is an object with a short lifetime. In Spring terms, we can use prototype scope here as it is smaller than a singleton. By lookup-method tag, we can indicate the name of the method, which will inject dependency.

Disadvantages and Advantages

It won’t be correct to compare this type of dependency injection with others as it has an absolutely different use case.

Conclusion

We looked at 4 types of dependency injection implemented by Spring framework:

  1. Constructor injection — good, reliable and immutable, inject via one of the constructors. Possible to configure in: XML, XML+Annotations, Java, Java + Annotations.
  2. Setter injection — more flexible, mutable objects, injection via setters. Possible to configure in: XML, XML+Annotations, Java, Java + Annotations.
  3. Field injection — fast and convenient, coupling with IoC container. Possible to configure in XML+Annotations, Java + Annotations.
  4. Lookup method injection — totally different from others, used for injection dependency of smaller scope. Possible to configure in: XML, XML+Annotations, Java, Java + Annotations.

Despite Constructor Injection being the recommended way, it is advisable to clarify your needs first. Maybe, setter or field injection would be more appropriate for your application. In any case, you can always mix different approaches and achieve your goals.

Difference between Constructor Injection and Setter Injection?

  1. In Setter Injection, partial injection of dependencies is possible. This means that if we have 3 dependencies like int, string, long, then it is not necessary to inject all 3 values if we use setter injection. If you do not inject them, then it will takes default values for those primitives.
  2. In Constructor Injection, partial injection of dependencies is not possible. The reason is, to call the constructor, we must pass all the arguments.
  3. Setter Injection will override the constructor injection value - if we write setter and constructor injection for the same property.
  4. But, Constructor Injection cannot override the setter injected values. It’s obvious because constructors are called to first to create the instance.
  5. Using Setter Injection you can not guarantee that certain dependency is injected or not, which means you may have an object with incomplete dependency.
  6. On other hand Constructor Injection does not allow you to construct object, until your dependencies are ready.
  7. In Constructor Injection, if Object A and B are dependent each other i.e A is depends on B and vice-versa, Spring throws ObjectCurrentlyInCreationException while creating objects of A and B because A object cannot be created until B is created and vice-versa.
  8. So spring can resolve circular dependencies through setter-injection because Objects are constructed before setter methods invoked.

External references

https://medium.com/@ilyailin7777/all-dependency-injection-types-spring-336da7baf51b

https://martinfowler.com/articles/injection.html#FormsOfDependencyInjection