This Java tutorial will help you understand the key concepts of dependency injection, step by step through simple code example - easy to understand and follow.

So, what is dependency injection?

It’s difficult to understand dependency injection in few sentences, so it’s better to begin with some code example.

Normally, a class depends on another class to do some work, for example:

public class ClientA {
	ServiceB service;
	
	public void doSomething() {		
		String info = service.getInfo();
	}
}
Here, class ClientA uses class ServiceB which is written as below, for example:

public class ServiceB {
	public String getInfo() {
		return "ServiceB’s Info";
	}
}
The class ClientA is said to be dependent on the class ServiceB, and ServiceB is called a dependency of ClientA. This kind of dependency is very trivial in programming. However, when the application’s code gets bigger and more complex, the hard-coded dependency among classes introduces some drawbacks:

  • The code is inflexible - it’s hard to maintain and extend as when a class permanently depends on another class, change to the depending class my require change to the dependent class. And it’s impossible to change the depending class later without updating and re-compiling the code.
  • The code is hard for unit testing because when you want to test only the functionalities of a class, you have to test other depending classes as well.
  • The code is hard for reuse because the classes are tightly coupled.
Therefore, dependency injection comes to address these drawbacks, making the code more flexible to changes, easy for unit testing and truly reusable. Then, how dependency injection works?

First, interfaces are used to define the types of the classes so its implementation can be changed later. For example, with the above code - the interfaces Client and Service are introduced:

public interface Client {
	void doSomething();
}

public interface Service {
	String getInfo();
}
 



Then the ServiceB class becomes an implementation of Service as below:

public class ServiceB implements Service {

	@Override
	public String getInfo() {
		return "ServiceB's Info";
	}
}
 

Then it’s possible to have different implementations of Service, for example ServiceC and ServiceD:

public class ServiceC implements Service {

	@Override
	public String getInfo() {
		return "ServiceC's Info";
	}
}

public class ServiceD implements Service {

	@Override
	public String getInfo() {
		return "ServiceD's Info";
	}
}
 

And the class ClientA is now implementing the Client interface and it uses the Service interface instead of a concrete class - the actual Service’s implementation is “injected” to this class via its constructor - constructor injection, as shown below:

public class ClientA implements Client {
	
	Service service;
	
	public ClientA(Service service) {
		this.service = service;
	}

	@Override
	public void doSomething() {
		
		String info = service.getInfo();
		
	}
}
 

The class ClientA is now not depending on any specific implementations of Service. Instead of creating an instance of dependent class directly in ClientA, the dependency injection container or framework is now responsible for creating that instance and inject it to the class ClientA via its constructor. For example:

Service service = new ServiceB();
Client client = new ClientA(service);
client.doSomething();
Here, an implementation of Service is ServiceB is created and passed to ClientA, which is not aware of the actual implementation it is using. ClientA only knows that the injected object is of type Service.

Besides constructor injection, setter injection is used to pass the depending object to the dependent one. Add the following setter method in the ClientA class:

public void setService(Service service) {
	this.service = service;
}
Then we can change to different Service’s implementation e.g. ServiceC like this:

((ClientA) client).setService(new ServiceC());
client.doSomething();
 

That’s a Java code example about dependency injection. Note that we write the code to create and inject the dependencies manually. In practice, a dependency injection container/framework like Spring will do the wiring automatically. You just declare the dependency information via XML file or annotations in Java classes, and the framework manages the dependencies for you.

 

Conclusion

Dependency injection is a technique that allows the client code to be independent from the services it is relying on. The client does not control how objects of the services are created - it works with an implementation of the service through interface. This is somewhat in inverse to trivial programming so dependency injection is also called inversion of control.

It makes the code more flexible, extensible, maintainable, testable and reusable - thus dependency injection is very popular in modern programming. In Java, dependency injection is supported since Java EE 6 - called CDI (Contexts and Dependency Injection). And the Spring framework is based on dependency injection, as well as other frameworks like Google Guice and Play.

 

 

Related Dependency Injection Tutorials:

 

Other Java Coding Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He started programming with Java in the time of Java 1.4 and has been falling in love with Java since then. Make friend with him on Facebook and watch his Java videos you YouTube.



Attachments:
Download this file (JavaDependencyExample.zip)JavaDependencyExample.zip[Java code example for dependency injection]3 kB

Add comment

   


Comments 

#7Minh Tran2021-07-17 20:36
Excellent explanation for this complicated topic.
Thanks
Quote
#6Nam2021-04-07 22:27
Hi Frieder,
The difference here is the use of interface in service classes - not concrete class. So the container or injector can put any real implementations.
Quote
#5Frieder2021-04-07 12:49
I dont really get the difference: Is it all about passing by an Object to an other Object? And then using Polymorphism? I guess one could do that aswell with the first expample: just write a setter, and then extend ServiceB with ServiceBA.
Quote
#4Ali2020-04-29 23:15
This is so unlike the example at www.javainuse.com/spring/sprbasic3
that does not use interfaces at all and still talks about loosely coupled.
Does that mean DI cannot be implemented without using interfaces ?
Quote
#3einstein2019-11-27 15:15
This is very very good source. Thank you very much, at the end I can understand the DI :)
Quote