Java Dependency Injection – DI Design Pattern Example Tutorial

Filed Under: Design Patterns
Java Dependency Injection

Java Dependency Injection design pattern allows us to remove the hard-coded dependencies and make our application loosely coupled, extendable and maintainable. We can implement dependency injection in java to move the dependency resolution from compile-time to runtime.

Java Dependency Injection

Java Dependency injection seems hard to grasp with theory, so I would take a simple example and then we will see how to use dependency injection pattern to achieve loose coupling and extendability in the application.

Let’s say we have an application where we consume EmailService to send emails. Normally we would implement this like below.


package com.journaldev.java.legacy;

public class EmailService {

	public void sendEmail(String message, String receiver){
		//logic to send email
		System.out.println("Email sent to "+receiver+ " with Message="+message);
	}
}

EmailService class holds the logic to send an email message to the recipient email address. Our application code will be like below.


package com.journaldev.java.legacy;

public class MyApplication {

	private EmailService email = new EmailService();
	
	public void processMessages(String msg, String rec){
		//do some msg validation, manipulation logic etc
		this.email.sendEmail(msg, rec);
	}
}

Our client code that will use MyApplication class to send email messages will be like below.


package com.journaldev.java.legacy;

public class MyLegacyTest {

	public static void main(String[] args) {
		MyApplication app = new MyApplication();
		app.processMessages("Hi Pankaj", "pankaj@abc.com");
	}

}

At first look, there seems nothing wrong with the above implementation. But above code logic has certain limitations.

  • MyApplication class is responsible to initialize the email service and then use it. This leads to hard-coded dependency. If we want to switch to some other advanced email service in the future, it will require code changes in MyApplication class. This makes our application hard to extend and if email service is used in multiple classes then that would be even harder.
  • If we want to extend our application to provide an additional messaging feature, such as SMS or Facebook message then we would need to write another application for that. This will involve code changes in application classes and in client classes too.
  • Testing the application will be very difficult since our application is directly creating the email service instance. There is no way we can mock these objects in our test classes.

One can argue that we can remove the email service instance creation from MyApplication class by having a constructor that requires email service as an argument.


package com.journaldev.java.legacy;

public class MyApplication {

	private EmailService email = null;
	
	public MyApplication(EmailService svc){
		this.email=svc;
	}
	
	public void processMessages(String msg, String rec){
		//do some msg validation, manipulation logic etc
		this.email.sendEmail(msg, rec);
	}
}

But in this case, we are asking client applications or test classes to initializing the email service that is not a good design decision.

Now let’s see how we can apply java dependency injection pattern to solve all the problems with the above implementation. Dependency Injection in java requires at least the following:

  1. Service components should be designed with base class or interface. It’s better to prefer interfaces or abstract classes that would define contract for the services.
  2. Consumer classes should be written in terms of service interface.
  3. Injector classes that will initialize the services and then the consumer classes.

Java Dependency Injection – Service Components

For our case, we can have MessageService that will declare the contract for service implementations.


package com.journaldev.java.dependencyinjection.service;

public interface MessageService {

	void sendMessage(String msg, String rec);
}

Now let’s say we have Email and SMS services that implement the above interfaces.


package com.journaldev.java.dependencyinjection.service;

public class EmailServiceImpl implements MessageService {

	@Override
	public void sendMessage(String msg, String rec) {
		//logic to send email
		System.out.println("Email sent to "+rec+ " with Message="+msg);
	}

}

package com.journaldev.java.dependencyinjection.service;

public class SMSServiceImpl implements MessageService {

	@Override
	public void sendMessage(String msg, String rec) {
		//logic to send SMS
		System.out.println("SMS sent to "+rec+ " with Message="+msg);
	}

}

Our dependency injection java services are ready and now we can write our consumer class.

Java Dependency Injection – Service Consumer

We are not required to have base interfaces for consumer classes but I will have a Consumer interface declaring contract for consumer classes.


package com.journaldev.java.dependencyinjection.consumer;

public interface Consumer {

	void processMessages(String msg, String rec);
}

My consumer class implementation is like below.


package com.journaldev.java.dependencyinjection.consumer;

import com.journaldev.java.dependencyinjection.service.MessageService;

public class MyDIApplication implements Consumer{

	private MessageService service;
	
	public MyDIApplication(MessageService svc){
		this.service=svc;
	}
	
	@Override
	public void processMessages(String msg, String rec){
		//do some msg validation, manipulation logic etc
		this.service.sendMessage(msg, rec);
	}

}

Notice that our application class is just using the service. It does not initialize the service that leads to better “separation of concerns“. Also use of service interface allows us to easily test the application by mocking the MessageService and bind the services at runtime rather than compile time.

Now we are ready to write java dependency injector classes that will initialize the service and also consumer classes.

Java Dependency Injection – Injectors Classes

Let’s have an interface MessageServiceInjector with method declaration that returns the Consumer class.


package com.journaldev.java.dependencyinjection.injector;

import com.journaldev.java.dependencyinjection.consumer.Consumer;

public interface MessageServiceInjector {

	public Consumer getConsumer();
}

Now for every service, we will have to create injector classes like below.


package com.journaldev.java.dependencyinjection.injector;

import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;

public class EmailServiceInjector implements MessageServiceInjector {

	@Override
	public Consumer getConsumer() {
		return new MyDIApplication(new EmailServiceImpl());
	}

}

package com.journaldev.java.dependencyinjection.injector;

import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.service.SMSServiceImpl;

public class SMSServiceInjector implements MessageServiceInjector {

	@Override
	public Consumer getConsumer() {
		return new MyDIApplication(new SMSServiceImpl());
	}

}

Now let’s see how our client applications will use the application with a simple program.


package com.journaldev.java.dependencyinjection.test;

import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.injector.EmailServiceInjector;
import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector;
import com.journaldev.java.dependencyinjection.injector.SMSServiceInjector;

public class MyMessageDITest {

	public static void main(String[] args) {
		String msg = "Hi Pankaj";
		String email = "pankaj@abc.com";
		String phone = "4088888888";
		MessageServiceInjector injector = null;
		Consumer app = null;
		
		//Send email
		injector = new EmailServiceInjector();
		app = injector.getConsumer();
		app.processMessages(msg, email);
		
		//Send SMS
		injector = new SMSServiceInjector();
		app = injector.getConsumer();
		app.processMessages(msg, phone);
	}

}

As you can see that our application classes are responsible only for using the service. Service classes are created in injectors. Also if we have to further extend our application to allow facebook messaging, we will have to write Service classes and injector classes only.

So dependency injection implementation solved the problem with hard-coded dependency and helped us in making our application flexible and easy to extend. Now let’s see how easily we can test our application class by mocking the injector and service classes.

Java Dependency Injection – JUnit Test Case with Mock Injector and Service


package com.journaldev.java.dependencyinjection.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector;
import com.journaldev.java.dependencyinjection.service.MessageService;

public class MyDIApplicationJUnitTest {

	private MessageServiceInjector injector;
	@Before
	public void setUp(){
		//mock the injector with anonymous class
		injector = new MessageServiceInjector() {
			
			@Override
			public Consumer getConsumer() {
				//mock the message service
				return new MyDIApplication(new MessageService() {
					
					@Override
					public void sendMessage(String msg, String rec) {
						System.out.println("Mock Message Service implementation");
						
					}
				});
			}
		};
	}
	
	@Test
	public void test() {
		Consumer consumer = injector.getConsumer();
		consumer.processMessages("Hi Pankaj", "pankaj@abc.com");
	}
	
	@After
	public void tear(){
		injector = null;
	}

}

As you can see that I am using anonymous classes to mock the injector and service classes and I can easily test my application methods. I am using JUnit 4 for the above test class, so make sure it’s in your project build path if you are running above test class.

We have used constructors to inject the dependencies in the application classes, another way is to use a setter method to inject dependencies in application classes. For setter method dependency injection, our application class will be implemented like below.


package com.journaldev.java.dependencyinjection.consumer;

import com.journaldev.java.dependencyinjection.service.MessageService;

public class MyDIApplication implements Consumer{

	private MessageService service;
	
	public MyDIApplication(){}

	//setter dependency injection	
	public void setService(MessageService service) {
		this.service = service;
	}

	@Override
	public void processMessages(String msg, String rec){
		//do some msg validation, manipulation logic etc
		this.service.sendMessage(msg, rec);
	}

}

package com.journaldev.java.dependencyinjection.injector;

import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;

public class EmailServiceInjector implements MessageServiceInjector {

	@Override
	public Consumer getConsumer() {
		MyDIApplication app = new MyDIApplication();
		app.setService(new EmailServiceImpl());
		return app;
	}

}

One of the best example of setter dependency injection is Struts2 Servlet API Aware interfaces.

Whether to use Constructor based dependency injection or setter based is a design decision and depends on your requirements. For example, if my application can’t work at all without the service class then I would prefer constructor based DI or else I would go for setter method based DI to use it only when it’s really needed.

Dependency Injection in Java is a way to achieve Inversion of control (IoC) in our application by moving objects binding from compile time to runtime. We can achieve IoC through Factory Pattern, Template Method Design Pattern, Strategy Pattern and Service Locator pattern too.

Spring Dependency Injection, Google Guice and Java EE CDI frameworks facilitate the process of dependency injection through use of Java Reflection API and java annotations. All we need is to annotate the field, constructor or setter method and configure them in configuration xml files or classes.

Benefits of Java Dependency Injection

Some of the benefits of using Dependency Injection in Java are:

  • Separation of Concerns
  • Boilerplate Code reduction in application classes because all work to initialize dependencies is handled by the injector component
  • Configurable components makes application easily extendable
  • Unit testing is easy with mock objects

Disadvantages of Java Dependency Injection

Java Dependency injection has some disadvantages too:

  • If overused, it can lead to maintenance issues because the effect of changes are known at runtime.
  • Dependency injection in java hides the service class dependencies that can lead to runtime errors that would have been caught at compile time.

That’s all for dependency injection pattern in java. It’s good to know and use it when we are in control of the services.

Comments

  1. Nyng says:

    TL;DR: DI just put all those connection points into an Object.

    1. Marcel says:

      you obviously didn’t understand the concept

  2. Alejandro Carlstein says:

    Great article and well explained!

    Would you consider to add a few diagrams to this article?
    Or, at least, one diagram before and after the inversion of dependencies?

  3. Saravanan says:

    A great article. A message for those who don’t understand the advantage as I did. It can look confusing at first and why we need so much classes and interfaces. But when you are going to test them as individual components you will know the advantage of it. You no more need to modify classes when there is a new change If we use this pattern we can avoid rewriting test cases again.

  4. Johann says:

    This way of implementing DI is way too convoluted.and makes code readability very complex. A simple factory pattern is all you need. If you’re developing for Android, use build variants to mock out code modules.

  5. tangara says:

    I’d like to know how does this fit into the MVC models?

    Will we have 4 or even 5 layers then ?

  6. Goran Pjanic says:

    I can see that for some people the example is not completely clear (some are even boring), so I’d like to clarify something.

    One of the principles of object-oriented design says: “Classes should be open for extension but closed for modification”

    What would that mean in our case? .

    For example, if we want to add a new messaging system, e.g. ViberService, the existing code should not be changed, but only create a new ViberServiceImpl class that implements MessageService and create a new ViberServiceInjector class, and everything will work fine.

    In this way we follow another principle of object programming: “Strive for loosely coupled designs
    between objects that interact”.

  7. Satya Betha says:

    Very nice explanation

  8. JohnsonMa says:

    Really helpful,thank you!

    1. mohan says:

      I am finding hard to understand. We are still initializing EmailServiceInjector which initialize EmailServiceImplementation in MyApplication class that mean when your adding new messaging service you still need do code change on myapplication class which is contracted to objective of decoupling hard coded dependency myapp class.

  9. Fagun Patel says:

    Awesome…..Thanks a lot.

  10. Hrishikesh Raskar says:

    Thanks for the post. Hope you will continue such work.

  11. Akshay shingan. says:

    well explained…thank you.

  12. Gunjan says:

    Thank you for the explanation

  13. amir elhajjam says:

    very helpful thank u

  14. Mukund says:

    very helpful and easy to understand for beginners

  15. Karthik says:

    confusing, lengthy and boring!

  16. Priya says:

    Well Explained tutorial of Dependency Injection . Thanks for Sharing

  17. saeed says:

    Thanks for your great article but your consumer class has 1 dependency(MessagingService interface). what if consumer class have multiple dependency or constructor params???

  18. Lord Banks says:

    I think its rather confusing. I’ve seen more detailed but easier to follow examples. Good job still

    1. Raccoon says:

      Please share the links to them.

  19. john says:

    very bad explanation, very intuitive and confusing code, and website font is too large. please get better or refrain from writing “Tutorials” on the internet.

  20. Gayn Dushantha says:

    Thank you!

  21. Yaffa Harari says:

    so much clear and clean
    thank you! 🙂

  22. Moustafa zein says:

    Well done

    1. Srini says:

      Very helpful material

  23. vinod says:

    Excellent Job bro

  24. pidgey says:

    Nice tutorial. But I think your example is just fabric pattern.

    1. Eduardo Ponzoni says:

      Absolutely agreed! For me this is nothing but a factory as there’s no dependency injection as such anywhere.

  25. Nix says:

    Nice article. Dependency Injection seems like Bridge Design Patter,

  26. Sree says:

    Good Article Pankaj!!!.

  27. Jagadeesh says:

    Simply Awesome…

  28. sivateja says:

    The best tutorial for dependency injection , Very well explanation, I am very thankfull to you Pankaj sir.

  29. paweu says:

    Well done.

  30. Sprung says:

    Way too long. DI via service should not take this long to explain.

    1. progger says:

      Actually I prefer this explanation to the others I found so far, because he actually goes to the effort of defining interfaces and changing the client to use those. I find other tutorials confusing in that respect, where, the clients continue to use the same concrete classes in the constructor or setter method, although the point of DI is to reduce those dependencies.

  31. HuangWei says:

    Wonderful, this post help me a lot to understand the DI technology which is the base of Spring FW.

    1. Pankaj says:

      Thanks for liking it, appreciate your comment.

  32. Vasily says:

    This is how to take a simple program and turn it into a swollen non-readable over engineered crap. Good job bro 😉

    1. tomacco says:

      “simple program” is not that simple when you are constructing a whole system above it. This kind of decisions increase maintainability and extensibility, meaning a lot of money saved in the mid – long term. But of course, If you are building toys, of course you don’t need to use any of these techniques.

      1. Olu says:

        Well said. That’s how you know the newbies 🙂

  33. sachindra pandey says:

    Good for Beginner and for Interview purpose .

  34. Ashish says:

    very nice explanation ………..

  35. Nikhil says:

    Excellent Article for begineer

  36. Virender says:

    How an Injector is different from a factory here?

    An injector here is basically serving the factory pattern. I think there more to Injector patter then explained here. Do you have a follow up tutorial to explain more?

    Thanks
    Virender

  37. EAT WORK says:

    I have to say there is something that I don’t understand.

    You claim that one way (without DI) is to provide the service in the constructor, but you say that the clients then need to decide which service to provider and this is not a good practice.

    But in the DI example, you do provide the service in the constructor, only that the client does not initiate the type of service

    1. david won says:

      I agree with all you guys that the DI example does nothing better than the non-DI example.
      And if something does make difference in this example, that is all credited to “interface” mechanism in java language itself.

  38. Kalinga says:

    Please put the link for the source code zip in the beginning.
    That will prevent someone making copy paste to create source code files and latter realize
    the entire effort has gone for toss as the source code is already shared as zip.

    Thanks,
    Kalinga

    1. Vipin says:

      source code must not be provided at all. What kind of coder you are if you too lazy to write it by own and practice.

  39. passage2 says:

    Hi Pankaj,
    Quick question about your example. You are saying

    //Send email
    injector = new EmailServiceInjector();
    app = injector.getConsumer();
    app.processMessages(msg, email);

    //Send SMS
    injector = new SMSServiceInjector();
    app = injector.getConsumer();
    app.processMessages(msg, phone);

    As you can see that our application classes are responsible only for using the service. Service classes are created in injectors. Also if we have to further extend our application to allow facebook messaging, we will have to write Service classes and injector classes only.

    So dependency injection implementation solved the problem with hard-coded dependency and helped us in making our application flexible and easy to extend.

    ###
    But this code still has a hard-coded injector that we need.
    injector = new EmailServiceInjector();

    My question: What is the difference between creating in my code an instance of Injector or an instance of Service? Both are hard-coded.
    If I understand the DI concept correctly, to avoid hard-coded dependency, which Service to use should be resolved at run-time, no?

    Could you please explain.
    Thanks.

    1. Ram Sharma says:

      Yes, it is hardcoded, but at some point of time you have to specify which service that you want to create, whether an Email service or facebook service or Twitter service, and using DI we are not changing application class. Application class or the Consumer class only takes service as input and for each service there exist an Injector.
      It is hard coded but in an intelligent way to separate the concerns.

      1. Ragu says:

        @Pankaj: Thanks for the wonderful tutorial. It is really helpfull. I’m still facing few confusions.
        May be because I’m new to this technology :). Still Thanks a lot for your effort.

        @Ram:

        Hi,:)

        You were right that at some point of time we have to specify which service that we want to use.
        But, what I’m not understanding is

        In Injector class,

        public class EmailServiceInjector implements MessageServiceInjector {

        @Override
        public Consumer getConsumer() {

        //Instead of below
        return new MyDIApplication(new EmailServiceImpl());

        //We can also use this (Also with method return type change)
        return new EmailServiceImpl();
        }
        }

        As mentioned in Tutorial, for new service class (like FB message service ) only corresponding Injector need to be created.
        Same is satisfied in the above approach.
        So what is the point creating a consumer class which can hold all service type
        and creating object of that service type and then passing required service type into it?

        Could you please make me understand this?

  40. Cris says:

    hey, good explanation about dependency injection, but the thing that I don’t understand well is, why a injector? why not just create a specific xxxServiceImp? we can also can mock/fake that object in our test, instead create a messageInjector we can create directly a MessageService (still mock/fake) and in our app we can have also something like this:

    Consumer app = null;

    //Send email
    app = new MessageServiceImp();
    app.processMessages(msg, email);

    //Send SMS
    app = new SMSServiceImp();
    app.processMessages(msg, phone);

    which one is the real benefice to use that Injector clases?

    1. Bret says:

      Same question for me as above. What benefit do the injector classes give you vs. above code.

    2. Ivan says:

      The same question for me, I don’t know why so many people saying this example is simple, not sure what’s the purpose of Injector in this example?

  41. Sushant says:

    Please add “Previous” and “Next” button at the end of every page.

  42. Sriprem says:

    Have gone through so many samples but got an clear idea about DI through this article.Really helpful, good work dude.

  43. M says:

    A few points to re-consider, just for the sake-of-argument:

    Quote:
    <>

    Comment:
    I think, “Testing the application” will not be difficult. The correct statement would be “Testing different scenarios” E.G. testing different messaging scenario. Testing the application itself would still be easy (without DI).

    Quote:
    <>

    Comment:
    Here is the catch. Our application classes are not only responsible for using the service. They are actually responsible for instantiating the injector classes also. Here is an explanation:

    MyMessageDITest -> new EmailServiceInjector() -> new EmailServiceImpl()
    MyMessageDITest -> new SMSServiceInjector() -> new SMSServiceImpl()

    Therefore, a question can be raised (asked mostly at the interview): “Why do we need a ServiceInjector object to instantiate MessageService object? After all, we are instantiating the ServiceInjector object anyway. For two different MessageService requirement, we are creating two instances of ServiceInjector [new EmailServiceInjector() and new SMSServiceInjector()]. If we are to create a new instance of ServiceInjector, can’t we create a new instance of MessageService?”

    Any thoughts? Please correct me if my understanding is wrong.

  44. pankaj says:

    very nice explanation. I have a question. Why it is a bad design to create the service in client code and pass it to the constructor of the Application. Because we are ultimately creating injector related to that service in the client code, so how it is different?

    Thank u

  45. rizwan says:

    Sir please create pdf book of your all spring tutorial liked design patterns book.

  46. tester says:

    I don’t get it. It does not look simple at all.
    Too many lines of code for this simple functionality

    1. tomboy says:

      Tester, try to write 50 different applications that all need to send email and do it without dependency injection.

      1. George says:

        This is the most lucid explanation, that can actually be understood on the first read (I tried a few before settled on this one).

        However, I think ‘Tester’ objected to complexity of implementation of dependency injection in strictly object-oriented language:
        — 1 interface
        — n classes declaring that each implements that interface
        — n implementations of that interface in each class
        — n injector classes
        — 1 application class
        — 1 consumer class

        What is needed isn’t more classes, but better language with functions as first-class objects. In such language you don’t need dependency injection to be explained and remembered; it comes naturally.

        To understand why pure object-oriented languages are a dead-end all one need is to read descriptions of dependency injection. Object-oriented is useful technique – it is not a successful methodology.

        But I admit, talks about this or that pattern is excellent marketing tool for outsourcing companies.

  47. Lanito says:

    That’s fine, but this is not mocking it is faking.
    Best Regards

  48. Rafał says:

    Good job!
    This video is also very helpful: https://www.youtube.com/watch?v=GB8k2-Egfv0

    1. Ayaz says:

      I would say the video tutorial is a perfect and easiest explaination. It is easiest because we use a framework which simplifies lots of stuff.

  49. AJ says:

    Job very well done. I am an experienced developer, but I was always confused about Dependency Injection pattern. Your example is simple and well-communicated, it now totally makes sense. Thanks. I have already subscribed to your mailing list and just downloaded your eBook – thanks for the hard work you put in.

  50. theodore says:

    Dude thanks for your time. Very nice tut and good explained! Greetings from Greece!

  51. Snehal Masne says:

    Excellent tutorial for DI with simple example, exactly what one expects at the beginging. Keep it up.

  52. Calderas says:

    Another Thanks from Mexico.

  53. Shashi Kanth says:

    Thank you for your effort.
    A very concise and neatly explained article indeed.

  54. Vijay says:

    Nice Article !

  55. Ahmed Kamal says:

    Good and clear article. Thanks Pankaj for your great efforts.

  56. Mustapha Naciri says:

    Thank’ you very much for teach us your big knowledge in java

  57. Thanga says:

    Well explained.

Leave a Reply

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

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages