Core Java

Dependency Injection Java Example

In this article, we will examine Dependency Injection in Java and some dependency injection examples.

You can also check this tutorial in the following video:

Java Dependency Injection Tutorial – video

1. Introduction

In a previous article, we explored the topic of Inversion of Control and how this design principle is appropriately suited for creating loosely coupled software applications. The IoC principle states that certain tasks typically performed by a class – for example, creating objects – should be consigned to an external entity, such as a container. The result is an application that is configurable, modular, extensible, and easier to test.

But how is IoC implemented? There are several design patterns available to implement IoC. These design patterns include:

  • Service Locator
  • Factory
  • Strategy
  • Template Method
  • Dependency Injection

In this article, we will cover Dependency Injection.

1.1 Technologies Used

Eclipse IDE for Enterprise Java Developers Version: 2020-03 (4.15.0)

2. Dependency Injection Java Example with Spring

2.1 Dependency Injection

In a software application, some objects (consumers) require the services of other objects to perform some task. This compels the consumer class to obtain a reference to the service class instance to call its methods. Therefore, the consumer class has a dependency on the service class.

Traditionally, the consumer class will create an instance of the service class using the new keyword. This makes the classes tightly coupled. A better approach is to delegate the responsibility of creating the dependency to another entity (typically a container) and having it pass in (inject) the object to the consumer via Dependency Injection.

2.2 Benefits of Using Dependency Injection

Some of the benefits of using DI are:

  • Your code is loosely coupled
  • You have less boilerplate code in your application
  • Adding and switching between implementations of a dependency is relatively simple
  • You can test your class in isolation by using mock objects
  • Your code is easier to maintain
  • Your code is easier to read

2.3 Dependency Injection and Spring

There are several DI frameworks available for Java applications. For example, there is CDI for Java EE and its reference implementation WELD. Another option is Google Guice. The most popular DI framework for Java is Spring.

Spring uses its IoC container to create and manage the objects that make up your application. The managed objects are known as beans in Spring jargon.

The IoC container is also responsible for injecting dependencies into the classes that require them. The BeanFactory interface and its subinterface ApplicationContext are used to interact with the IoC container. Note that the factory will inject a fully constructed bean, that is, if the injected bean itself has dependencies, they will be resolved before the bean is injected. Dependency Injection occurs at runtime.

To use dependency injection in a Spring application, the developer must do two things:

  1. Specify the components (beans) that will be injected into the dependent classes using metadata. This can be done via Annotation Configuration, Java Configuration, or XML Configuration. Doing so will inform the IoC container to construct and register the beans in the ApplicationContext at startup.
  2. Define constructors or setters in the consumer class with metadata to have those dependencies injected. (This is referred to as “autowiring” in Spring.)

Notes:

  • While XML configuration is still supported in Spring, Annotations Configuration and Java Configuration are typically used to provide the metadata used to wire your application.
  • Constructor-based and setter-based are the most common types of injection.  A field-based injection is supported but has fallen out of favor due to some undesired side effects. For example, you cannot test your class outside of the IoC container.

2.4 Example without Dependency Injection

To demonstrate how dependency injection works in Spring, we’ll first create a simple application with classes that instantiate their dependencies directly. We will then refactor the application to use dependency injection

In the New Project – Select a Wizard dialog box, expand Spring Boot and select Spring Starter Project. Click “Next”.

Dependency Injection Java - New Project
New Project

In the New Spring Starter Project dialog box, enter a name for the project. Also, enter the group, artifact, and package information. Select Java Version 11. Click “Next”.

Dependency Injection Java - New Spring Starter Project
New Spring Starter Project

In the New Spring Starter Project Dependencies dialog window, select “Spring Web” and click “Finish”.

Project Dependencies

Create a file quote.txt in a new folder /src/main/resources/files/ with the following contents:

quote.txt

You cannot escape the responsibility of tomorrow by evading it today
I think therefore I am
It was the best of times, it was the worst of times...
Don't cry because it's over, smile because it happened
Be yourself; everyone else is already taken
So many books, so little time

We will use this file in our sample application.

Next, create a class FileQuoteList with the following code:

FileQuoteList.java

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

public class FileQuoteList {

	final String fileName = "files/quote.txt";
	final File file = getFileFromResource(fileName);

	public List<String> getQuoteList() {
		
		try (FileReader reader = new FileReader(file); 
			 BufferedReader br = new BufferedReader(reader)) {		
			return br.lines().collect(Collectors.toList());
		} catch (IOException e) {
			return new ArrayList<String>();
		} 
	}
	
	File getFileFromResource(String fileName) {

		File quotes = null;
		Resource resource = new ClassPathResource(fileName);
		
		try {
			quotes = resource.getFile();
		} catch (IOException e) {
			e.printStackTrace();
			return quotes;
		}
		
		return quotes;
	}
}

FileQuoteList has a private method that reads a file from the classpath and a public method getQuoteList that returns a List of lines read from the file.

Next, create a service class RandomQuoteService with a public method that returns a random quote from the list.

RandomQuoteService.java

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Random;

public class RandomQuoteService {

	final FileQuoteList quote = new FileQuoteList();
	
	public String getRandomQuote() throws FileNotFoundException, IOException {
		List<String> quoteList = quote.getQuoteList();
		Random random = new Random();
		int index = random.nextInt(quoteList.size());
		return (String) quoteList.get(index);
	}
}

Notice that we are instantiating a FileQuote object directly to our class using the new operator.

Next, we’ll create a Spring controller RandomQuoteController.

RandomQuoteController.java

import java.io.IOException;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RandomQuoteController {
	
	RandomQuoteService randQ = new RandomQuoteService();

	@RequestMapping("daily-quote")
	public String getMyQuote() {
		try {
			return randQ.getRandomQuote();
		} catch (IOException e) {
			return "To be or not to be";
		}
	}
}

The call to daily-quote will return a random quote from the service or a static message if an exception occurs.

Start the application, open a browser, and navigate to localhost:8080/daily-quote.

Daily Quote

Recall that we hardcoded the FileQuoteList dependency in our RandomQuoteService class. This works, but what if you want to substitute FileQuoteList with a different implementation, say for testing purposes? As it stands, we need to modify RandomQuoteService every time we want to switch between the actual implementation and the test implementation. You can recognize how this is impractical.  A better approach to handle this predicament is to code to the interface and use dependency injection.

2.4 Example with Dependency Injection

Let’s refactor our code by extracting an interface from the existing implementation.  Open FileQuoteList. In the editor highlight FileQuoteList. Right-click and select Refactor -> Extract Interface…

Extract Interface

Enter QuoteList as the interface name and select getQuoteList() from Members to declare in the interface. Click OK.

QuoteList Interface

Next, create a new class MockQuoteList that implements QuoteList. Add the following code:

MockQuoteList.java

import java.util.List;

import org.springframework.stereotype.Component;

@Component
public class MockQuoteList implements QuoteList {

	@Override
	public List<String> getQuoteList() {
		 return List.of(
				 	"May the force be with you", 
		        	"There is no place like home", 
		        	"I'll be back",
		        	"You're going to need a bigger boat",
		        	"My precious");
	}

}

We can provide configuration metadata using annotation-based configuration. During application startup, Spring will scan specific packages for Java classes annotated with @Component and other specialized annotations. This process is known as component scanning.

Here are some of the specialized (stereotype) annotations Spring will seek during the component scan process:

  • @Controller
  • @Service
  • @Repository

Spring will automatically register these annotated classes as beans in the application context.

Next, let’s have Spring inject the QuoteList dependency into the RandomQuoteService using constructor-based injection. Open RandomQuoteService and modify it as follows:

RandomQuoteService.java

@Service
public class RandomQuoteService {

	final QuoteList quote;
	
	@Autowired
	public RandomQuoteService(QuoteList quote) {
		this.quote = quote;
	}
	
	public String getRandomQuote() throws FileNotFoundException, IOException {
		List<String> quoteList = quote.getQuoteList();
		Random random = new Random();
		int index = random.nextInt(quoteList.size());
		return (String) quoteList.get(index);
	}
}

Like @Component, the @Service annotation denotes this class as a bean to be managed by Spring.  Notice that we also changed the dependency to use an interface rather than the concrete class. (Actually, it was changed for us when we refactored FileQuoteList.) This will allow us to plugin any implementation of the QuoteList type.

Also, we are no longer instantiating a concrete implementation of QuoteList directly in the class. We will have the IoC container inject one for us. The @Autowired annotation decorating the constructor instructs Spring to look for a bean that matches the parameter type. If it finds a matching bean, it will inject it into the object.

Note: When using constructor-based injection, the @Autowired annotation is optional – Spring will automatically inject a matching bean.

Let’s also have Spring inject the RandomQuoteService into the controller. Modify RandomQuoteController as follows:

RandomQuoteController.java

@RestController
public class RandomQuoteController {
     
    RandomQuoteService randQ;
 
    @Autowired
    public void setRandQ(RandomQuoteService randQ) {
		this.randQ = randQ;
	}

	@RequestMapping("daily-quote")
    public String getMyQuote() {
        try {
            return randQ.getRandomQuote();
        } catch (IOException e) {
            return "To be or not to be";
        }
    }
}

We are using setter-based injection in the RandomQuoteService class.  This is achieved by decorating the setter method with the @Autowired annotation. Setter-based injection is best used when the dependency is optional, which may be true if we have other methods that do not require RandomQuoteService.

Restart the application and go back to your browser. You should now be seeing famous movie quotes.

2.5 Switching Between Implementations

If you want to be able to switch back to the FileQuoteList implementation, you will need to add the @Component annotation so that Spring adds it to the application context.

FileQuoteList.java

@Component
public class FileQuoteList implements QuoteList {
...
}

Restart the application.

2020-06-13 13:20:21.207  WARN 5392 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'randomQuoteController': Unsatisfied dependency expressed through method 'setRandQ' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'randomQuoteService' defined in file [C:\Users\Gilbert\workspaces\java12examples\di-spring\target\classes\com\jcg\examples\RandomQuoteService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.jcg.examples.QuoteList' available: expected single matching bean but found 2: fileQuoteList,mockQuoteList
2020-06-13 13:20:21.210  INFO 5392 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]


***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.jcg.examples.RandomQuote required a single bean, but 2 were found:
	- fileQuoteList: defined in file FileQuote.class]
	- mockQuoteList: defined in file MockQuoteList.class]
	

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

Wait, what? You are seeing this error because Spring does not know which implementation to inject into the service class since both beans fulfill the requirement. (Both are of type QuoteList.) So how do we resolve this? There are a few options that can be used to address this issue, as the error message suggests. The simplest fix is to use the @Primary annotation on the class you want to be injected. Modify FileQuoteList as follows:

FileQuoteList.java

@Component
@Primary
public class FileQuoteList implements QuoteList {
...
}

The @Primary annotation tells Spring, “Hey, I’m the primary bean that fills the requirement so use me”.

Restart the application. The RandomQuoteService is now using the primary bean.

If you want a truly configurable application that’s able to change implementations without modifying code, you can use Spring Profiles. Unfortunately, the topic is beyond the scope of this article.

2.5 Costs of Using Dependency Injection

As stated before, dependency injection occurs at runtime. This has some ramifications:

  • Errors that you would normally discover at compile time may not be apparent until you run your application
  • The overhead of injecting beans into your objects can increase your application’s startup time
  • The implementation used for an interface is hidden, conceivably making code maintenance challenging

You may want to evaluate these points when considering applying dependency injection in your application.

3. Summary

In this article, we covered the dependency injection in Java. We talked about the Dependency Injection design pattern and some of the benefits of using it in your applications. We demonstrated how dependency injection is implemented using Spring. Finally, we examined some of the possible drawbacks associated with using dependency injection in your application.

4. Download the Source Code

This was a Java Dependency Injection example with Spring.

Download
You can download the full source code of this example here:
Dependency Injection Java Example

Last updated on May 18th, 2021

Gilbert Lopez

Gilbert Lopez is an application developer and systems integration developer with experience building business solutions for large and medium-sized companies. He has worked on many Java EE projects. His roles have included lead developer, systems analyst, business analyst and consultant. Gilbert graduated from California State University in Los Angeles with a Bachelor of Science degree in Business.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Zahid Rahman
3 years ago

I hope this is the correct definition of dependency Injection. This is the definition I am going to base my understanding on..😎

Back to top button