What is Dependency Injection with Java Code Example
- Details
- Written by Nam Ha Minh
- Last Updated on 04 July 2019   |   Print Email
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:
- Spring Dependency Injection Example with XML Configuration
- Spring Dependency Injection Example with Annotations
- Spring Dependency Injection Example with Java Config
Other Java Coding Tutorials:
- How to implement forgot password feature for Java web application
- How to implement remember password (remember me) for Java web application
- How to code login and logout with Java Servlet, JSP and MySQL
- How to Code Hit Counter for Java web application
- 10 Common Mistakes Every Beginner Java Programmer Makes
- 10 Java Core Best Practices Every Java Programmer Should Know
- How to become a good programmer? 13 tasks you should practice now
- How to calculate MD5 and SHA hash values in Java
- Java File Encryption and Decryption Example
Comments
Thanks
The difference here is the use of interface in service classes - not concrete class. So the container or injector can put any real implementations.
that does not use interfaces at all and still talks about loosely coupled.
Does that mean DI cannot be implemented without using interfaces ?