Dependency Injection in Spring

1. Overview

In this article, we will discuss 3 different dependency injection strategy provided in Spring.

2. Content

Dependency Injection : 

Dependency means one object requires another class to work with. Injection means a way to provide the required dependency to an object.

Example: HashSet creates an instance of HashMap. The HashSet will only use keys of HashMap to maintain a Set of unique elements. 

The intent behind dependency injection is to have a separation of concern between constructor of objects and use of objects so we can have better readability and reusability of code.

The 3 different injection strategy are : 

  1. Setter Injection
  2. Field Injection
  3. Constructor Injection (Best way to add dependency)

You can mix and match all the three approaches, but that is a kludgy/inelegant way of injecting dependencies.

At the end of this article I will explain a few parting thoughts on DI. There are reasonable explanations on why to use field or setter injection, but none of them are as good as compared to constructor injection.

3. Setter Injection

Setter injection is achieved by calling the setter method. The caller to setter method will be the IOC container. The call is made only after invoking no args constructor.

Let us take an example. Scenario is, once you get a response from outbound service that you made a call to, you do two things.

  1. Check for response status code and throw an exception if response code is not what we expected.
  2. If the response code is 2XX i.e. Successful family, then just reads the response and returns it.

ErrorHandler class would look like this :

public class ErrorHandler {

	public void handleError(Response response) {
		if (response.getStatusInfo().getFamily() != Family.SUCCESSFUL) {
			throw new IllegalStateException();
		}
		// else do nothing.
	}

}

ResponseHandler class would look like this : 

@Named
public class ResponseHandler {

	private ErrorHandler errorHandler;

	@Inject
	public void setErrorHandler(ErrorHandler errorHandler) {
		this.errorHandler = errorHandler;
	}

	public Response handleResponse(Response response) {
		errorHandler.handleError(response);
		
		ClientResponse clientResponse = response.readEntity(ClientResponse.class);
		
		return Response.ok().entity(clientResponse).build();
	}

}

3.1 Reasons you shouldn’t use setter injection?

  • 3 lines of unnecessary boilerplate code per dependency added in class.
  • Dependencies added in class are unnecessarily mutable.
  • Class is also unnecessarily mutable because of the setter method.

4. Field Injection

In field injection we must declare a field that needs to be injected and annotate it with @Inject or @Autowired. This is the simplest of injection mechanisms.

@Named
public class ResponseHandler {

	@Inject
	private ErrorHandler errorHandler;

	public Response handleResponse(Response response) {
		errorHandler.handleError(response);
		
		ClientResponse clientResponse = response.readEntity(ClientResponse.class);
		
		return Response.ok().entity(clientResponse).build();
	}

}

Quite frankly, Field injection is the worst injection mechanism. Using this immediately leads to usage of Mockito. You may write tests like this.

@RunWith(MockitoJUnitRunner.class)
public class ResponseHandlerTest {
   @Mock
   private ErrorHandler errorHandler;


   @InjectMocks
   private ResponseHandler responseHandler;
   //Tests...
}

Now if you inject any additional dependency in the ResponseHandler class, your test case will break. Because mockito expects the mock of newly added dependency. This is annoying.

4.1 Reasons you shouldn’t use field injection.

  • The ultimate problem of field injection lurks in its simplicity. It looks cute enough to use it but can cause irreparable damage. The problem is, if there is any change and its dependency needs to be added you can add it easily. After few iterations you realize that the class has too many dependencies and does too many things. What you have now is a God class. Good luck refactoring it, if you ever intend to refactor.
  • Dependencies added in class are unnecessarily mutable.

5. Constructor Injection

In constructor injection, we will provide the dependencies in the constructor. 

@Named
public class ResponseHandler {

	private final ErrorHandler errorHandler;

	@Inject
	public ResponseHandler(ErrorHandler errorHandler) {
		this.errorHandler = errorHandler;
	}

	public Response handleResponse(Response response) {
		errorHandler.handleError(response);

		ClientResponse clientResponse = response.readEntity(ClientResponse.class);

		return Response.ok().entity(clientResponse).build();
	}

}

This is the best way to inject dependencies.

5.1 Reasons you must use constructor injection

  • We can mark all injected dependencies as private final.
  • We can make all dependencies immutable.
  • If the constructor accepts too many arguments, you know that this class is a high coupling class and hence needs to be refactored.

Constructor Injection is recommended by Spring Team. Read here.

6. Few words on field and constructor injection

Using any injection mechanisms won’t give you problems when you write code. It may sound obvious to add more dependencies when you write code. But once you finish writing it take a glance at the code you wrote. If it does not satisfy you then you need to refactor.

I think there can be few arguments in favor of setter and field injection. But I still don’t agree with them.

Argument 1: I have used Setter/Field injection for years and never had a problem. Why would I use constructor injection?

  1. First, constructor injection is not an obvious choice, or maybe we don’t even know that it exists. I for one didn’t know that there was constructor injection. Silly me. 
  2. If you are using @Required annotation on setter(setter injection), why just not use Constructor injection. Because you cannot create instances if other dependencies are not created. No need for boiler plate code. 

Argument 2: With setter injection, I can easily provide a mock to it.

  1. Why do you need a mock? Why do you need Mockito? Can’t you use a plain simple dependency injection in your test?

Argument 3: With setter/field injection I can inject several dependencies. If I do so with Constructor injection, then my code looks ugly.

  1. Excellent point. If you have more arguments in your constructor, then your class is highly coupled. You need to break it apart so the code doesn’t become god class.

7. Conclusion

That is all on the dependency injection mechanism in Spring. Favor constructor injection as against setter or field injection.

Leave a Reply

Your email address will not be published. Required fields are marked *