Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Java 9 Dependency Injection
Java 9 Dependency Injection

Java 9 Dependency Injection: Write loosely coupled code with Spring 5 and Guice

Arrow left icon
Profile Icon Krunal Patel Profile Icon Nilang Patel
Arrow right icon
€36.99
Full star icon Full star icon Full star icon Full star icon Empty star icon 4 (3 Ratings)
Paperback Apr 2018 246 pages 1st Edition
eBook
€20.98 €29.99
Paperback
€36.99
Subscription
Free Trial
Renews at €18.99p/m
Arrow left icon
Profile Icon Krunal Patel Profile Icon Nilang Patel
Arrow right icon
€36.99
Full star icon Full star icon Full star icon Full star icon Empty star icon 4 (3 Ratings)
Paperback Apr 2018 246 pages 1st Edition
eBook
€20.98 €29.99
Paperback
€36.99
Subscription
Free Trial
Renews at €18.99p/m
eBook
€20.98 €29.99
Paperback
€36.99
Subscription
Free Trial
Renews at €18.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Table of content icon View table of contents Preview book icon Preview Book

Java 9 Dependency Injection

Why Dependency Injection?

In software development, very often someone else might already have found effective solutions to the problems you are facing.

As a developer, you don't need to reinvent the wheel every time. Instead, you need to refer to the well-established practices and methodologies. Have you guessed what we are talking about? That's correct: design patterns.

This chapter is crafted to cover the following interesting topics:

  • What design patterns are and their benefits
  • Dependency Injection Principle (DIP)
  • Inversion of Control (IoC)—a design methodology to implement DIP
  • Various design patterns to implement IoC
  • Dependency Injection (DI)
  • Various types to implement DI
  • How an IoC container is helpful to apply a DI

Design patterns

By definition, a design pattern is a set of proven de facto industry standards and best practices for resolving recurring problems. Design patterns are not ready-made solutions. Rather, they're a way or template to implement and apply the best possible solution for your problem.

It's equally true that if a design pattern is not implemented in the right way, it creates a lot of problems rather than solving the one you expected to solve. So it's very important to know which design pattern, if any, is right for a specific scenario.

Design patterns are a common paradigm to describe the problem and how to solve it. It's usually not language specific. Design patterns can protect you from the design problems that generally occur in the later stages of development.

There are numerous advantages to using design patterns, as follows:

  • Improves software reusability
  • Development cycle becomes faster
  • Makes the code more readable and maintainable
  • Increases the efficiency and enhances the overall software development
  • Provides common vocabulary to describe problems and best possible solutions in a more abstract way

And you can count many more. In the following sections, we will gain a deep understanding of how to make your code modular, loosely coupled, independent, testable, and maintainable, by following certain principles and patterns.

This chapter will cover in-depth ideas about the Dependency Inversion Principle (DIP), the Inversion of Control paradigm, and DI design pattern.

Most developers use the terms design principle and design pattern interchangeably, even though there is a difference between them.

Design principle: Generically, this is a guideline about what is the right way and what is the wrong way to design your application. Design principles always talk about what to do instead of how to do it.

Design patterns: A generic and reusable solution for commonly occurring problems. Design patterns talk about how to solve the problems in a given software design context by providing clear methodologies.

The first step towards making your code cleaner, readable, decoupled, maintainable, and modular is to learn the design principle called DIP.

Dependency Inversion Principle

DIP provides high-level guidance to make your code loosely coupled. It says the following:

  • High-level modules should not depend on low-level modules for their responsibilities. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.

Changes are always risky when they're made in dependent code. DIP talks about keeping a chunk of code (dependency) away from the main program to which it is not directly related. 

To reduce the coupling, DIP suggests eliminating the direct dependency of low-level modules on high-level modules to perform their responsibilities. Instead, make the high-level module rely on abstraction (a contract) that forms the generic low-level behavior. 

This way, the actual implementation of low-level modules can be changed without making any changes in high-level modules. This produces great flexibility and molecularity in the system. As far as any low-level implementation is bound to abstraction, high-level modules can invoke it.

Let's have a look at a sample suboptimal design where we can apply DIP to improve the structure of the application.

Consider a scenario where you are designing a module that simply generates balance sheets for a local store. You are fetching data from a database, processing it with complex business logic, and exporting it into HTML format. If you design this in a procedural way, then the flow of the system would be something like the following diagram:

A single module takes care of fetching data, applying business logic to generate balance sheet data, and exporting it into HTML format. This is not the best design. Let's separate the whole functionality into three different modules, as shown in the following diagram:

  • Fetch Database Module : This will fetch data from a database
  • Export HTML Module:  This will export the data in HTML
  • Balance Sheet Module: This will take data from a database module, process it, and give it to the export module to export it in HTML

In this case, the balance sheet module is a high-level module, and fetch database and export HTML are low-level modules.

The code of the FetchDatabase module should look something like the following snippet:

public class FetchDatabase {
public List<Object[]> fetchDataFromDatabase(){
List<Object[]> dataFromDB = new ArrayList<Object[]>();
//Logic to call database, execute a query and fetch the data
return dataFromDB;
}
}

The ExportHTML module will take the list of data and export it into HTML file format. The code should look as follows:

public class ExportHTML {
public File exportToHTML(List<Object[]> dataLst){
File outputHTML = null;
//Logic to iterate the dataLst and generate HTML file.
return outputHTML;
}
}

The code for our parent module—the BalanceSheet module that takes the data from the fetch database module and sends to the export HTML module—should look as follows:

public class BalanceSheet {
private ExportHTML exportHTML = new ExportHTML();
private FetchDatabase fetchDatabase = new FetchDatabase();

public void generateBalanceSheet(){
List<Object[]> dataFromDB =
fetchDatabase.fetchDataFromDatabase();
exportHTML.exportToHTML(dataFromDB);
}
}

At first glance, this design looks good, as we separated the responsibilities of fetching and exporting the data into individual child modules. Good design can accommodate any future changes without breaking the system. Will this design make our system fragile in case of any future changes? Let us have a look at that.

After some time, you need to fetch the data from external web services along with the database. Also, you need to export the data in PDF format rather than HTML format. To incorporate this change, you will create new classes/modules to fetch data from web services and to export the PDF as per the following snippet:

// Separate child module for fetch the data from web service.
public class FetchWebService {
public List<Object[]> fetchDataFromWebService(){
List<Object[]> dataFromWebService = new ArrayList<Object[]>();
//Logic to call Web Service and fetch the data and return it.
return dataFromWebService;
}
}
// Separate child module for export in PDF
public class ExportPDF {
public File exportToPDF(List<Object[]> dataLst){
File pdfFile = null;
//Logic to iterate the dataLst and generate PDF file
return pdfFile;
}
}

To accommodate the new ways of fetching and exporting data, the balance sheet module needs some sort of flag. Based on the value of this flag, the respective child module will be instantiated in the balance sheet module. The updated code of the BalanceSheet module would be as follows:

public class BalanceSheet {

private ExportHTML exportHTML = null;
private FetchDatabase fetchDatabase = null;
private ExportPDF exportPDF = null;
private FetchWebService fetchWebService = null;

public void generateBalanceSheet(int inputMethod, int outputMethod){

//1. Instantiate the low level module object.
if(inputMethod == 1){
fetchDatabase = new FetchDatabase();
}else if(inputMethod == 2){
fetchWebService = new FetchWebService();
}

//2. fetch and export the data for specific format based on flags.
if(outputMethod == 1){
List<Object[]> dataLst = null;
if(inputMethod == 1){
dataLst = fetchDatabase.fetchDataFromDatabase();
}else{
dataLst = fetchWebService.fetchDataFromWebService();
}
exportHTML.exportToHTML(dataLst);
}else if(outputMethod ==2){
List<Object[]> dataLst = null;
if(inputMethod == 1){
dataLst = fetchDatabase.fetchDataFromDatabase();
}else{
dataLst = fetchWebService.fetchDataFromWebService();
}
exportPDF.exportToPDF(dataLst);
}

}
}

Great work! Our application is able to handle two different input and output methods to generate balance sheets. But wait a minute; what happens when you need to add more  methods (fetch and export data) in the future? For example, you might need to fetch the data from google drive and export the balance sheet in Excel format.

For every new method of input and output, you need to update your main module, the balance sheet module. When a module is dependent on another concrete implementation, it's said to be tightly coupled on that. This breaks the fundamental principle: open for extension but closed for modification.

Let's recall what DIP talks about: high-level modules should not depend on low-level modules for their responsibilities. Both should depend on abstractions.

This is the fundamental problem in our design. In our case, the balance sheet (high-level) module tightly depends on fetch database and export HTML data (low-level) modules.

As we have seen, principles always show the solution to design problems. It doesn't talk about how to implement it. In our case, DIP talks about removing the tight dependency of low-level modules on high-level modules.

But how do we do that? This is where IoC comes into the picture. IoC shows a way of defining abstraction between modules. In short, IoC is the way to implement DIP.

Inversion of Control

IoC is a design methodology used to build a loosely coupled system in software engineering by inverting the control of flow from your main program to some other entity or framework.

Here, the control refers to any additional activities a program is handling other than its main activities, such as creating and maintaining the dependency objects, managing the application flow, and so on.

Unlike procedural programming style, where a program handles multiple unrelated things all together, IoC defines a guideline where you need to break the main program in multiple independent programs (modules) based on responsibility and arrange them in such a way that they are loosely coupled.

In our example, we break the functionality into separate modules. The missing part was how to arrange them to make them decoupled, and we will learn how IoC makes that arrangement. By inverting (changing) the control, your application becomes decoupled, testable, extensible, and maintainable.

Implementing DIP through IoC

DIP suggests that high-level modules should not depend on low-level modules. Both should depend on abstraction. IoC provides a way to achieve the abstraction between high-level and low-level modules.

Let's see how we can apply DIP through IoC on our Balance Sheet example. The fundamental design problem is that high-level modules (balance sheet) tightly depend on low-level (fetch and export data) modules.

Our goal is to break this dependency. To achieve this, IoC suggests inverting the control. In IoC, inverting the control can be achieved in the following ways:

  • Inverting the interface: Make sure the high-level module defines the interface, and low-level modules follow it
  • Inverting object creation: Change the creation of dependency from your main modules to some other program or framework
  • Inverting flow: Change the flow of application

Inverting the interface

Inverting the interface means inverting the interaction control from low-level modules to high-level modules. Your high-level module should decide which low-level modules can interact with it, rather than keep changing itself to integrate each new low-level module.

After inverting the interface, our design would be as per the following diagram:

In this design, the balance sheet module (high-level) is interacting with fetch data and export data (low-level) modules with common interface. The very clear benefits of this design are that you can add new fetch data and export data (low-level) modules without changing anything on the balance sheet module (high-level).

As far as low-level modules are compatible with the interface, the high-level modules will be happy to work with it. With this new design, high-level modules are not dependent on low-level modules, and both are interacting through an abstraction (interface). Separating the interface from the implementation is a prerequisite to achieve DIP.

Let's change our code as per this new design. First, we need to create two interfaces: to fetch the data and export the data as follows:

public interface IFetchData {
//Common interface method to fetch data.
List<Object[]> fetchData();
}
public interface IExportData {
//Common interface method to export data.
File exportData(List<Object[]> listData);
}

Next, all low-level modules must implement these interfaces as per the following snippet:

public class FetchDatabase implements IFetchData {
public List<Object[]> fetchData(){
List<Object[]> dataFromDB = new ArrayList<Object[]>();
//Logic to call database, execute a query and fetch the data
return dataFromDB;
}
}

public class FetchWebService implements IFetchData {
public List<Object[]> fetchData(){
List<Object[]> dataFromWebService = new ArrayList<Object[]>();
//Logic to call Web Service and fetch the data and return it.
return dataFromWebService;
}
}

public class ExportHTML implements IExportData{
public File exportData(List<Object[]> listData){
File outputHTML = null;
//Logic to iterate the listData and generate HTML File
return outputHTML;
}
}
public class ExportPDF implements IExportData{
public File exportData(List<Object[]> dataLst){
File pdfFile = null;
//Logic to iterate the listData and generate PDF file
return pdfFile;
}

}

Finally, the balance sheet  module needs to rely on interfaces to interact with low-level modules. So the updated BalanceSheet module should look like the following snippet:

public class BalanceSheet {
private IExportData exportDataObj= null;
private IFetchData fetchDataObj= null;

public Object generateBalanceSheet(){
List<Object[]> dataLst = fetchDataObj.fetchData();
return exportDataObj.exportData(dataLst);
}
}

You may have observed that, the generateBalanceSheet() method became more straightforward. It allows us to work with additional fetch and export modules without any change. It is thanks to the mechanism of inverting the interface that makes this possible.

This design looks perfect; but still, there is one problem. If you noticed, the balance sheet module is still keeping the responsibility of creating low-level module objects (exportDataObj and fetchDataObj). In other words, object creation dependency is still with the high-level modules.

Because of this, the Balance Sheet module is not 100 percent decoupled from the low-level modules, even after implementing interface inversion. You will end up instantiating low-level modules with if/else blocks based on some flag, and the high-level module keeps changing for adding additional low-level modules integration.

To overcome this, you need to invert the object creation from your higher-level module to some other entity or framework. This is the second way of implementing IoC.

Inverting object creation 

Once the abstraction between modules is set, there is no need to keep the logic of creating dependency objects in higher-level modules. Let us understand the importance of inversion of object creation design with one more example.

Suppose you are designing a war game. Your player can shoot the enemy with various weapons. You created separate classes (low-level module) for each of the weapons. While playing the game, your player can add the weapon based on points earned.

Also, the player can change the weapon. To implement inversion of interface, we created an interface called Weapon, which will be implemented by all weapon modules, as per the following diagram:

Assume that there are three weapons initially that you kept in the game. If you keep weapon creation code in your player module, the logic of choosing a weapon would be as per the following snippet:

public class Player {
private Weapon weaponInHand;
public void chooseWeapon(int weaponFlag){
if(weaponFlag == 1){
weaponInHand = new SmallGun();
}else if(weaponFlag ==2){
weaponInHand = new Rifle();
}else{
weaponInHand = new MachineGun();
}
}

public void fireWeapon(){
if(this.weaponInHand !=null){
this.weaponInHand.fire();
}
}
}

Since the player module is taking care of creating the object of weapons, we are passing a flag in the chooseWeapon() method. Let us assume that, over a period of time, you add a few more weapons to the game. You end up changing the code of the Player module every time you add a new weapon.

The solution to this problem is to invert the object creation process from your main module to another entity or framework.

Let's first apply this solution to our Player module. The updated code would be as follows:

public class Player {
private Weapon weaponInHand;
public void chooseWeapon(Weapon setWeapon){
this.weaponInHand = setWeapon;
}

public void fireWeapon(){
if(this.weaponInHand !=null){
this.weaponInHand.fire();
}
}
}

You can observe the following things:

  • In the chooseWeapon() method, we are passing the object of weapons through the interface. The Player module is no longer handling the creation of weapon objects.
  • This way, the Player (higher-level) module is completely decoupled from Weapon (low-level) modules.
  • Both modules interact through the interface, defined by higher-level modules.
  • For any new weapon added into the system, you do not need to change anything in the player module.

Let's apply this solution (invert creating object) to our balance sheet module. The updated code for the BalanceSheet module would be as per the following snippet:

public class BalanceSheet {

private IExportData exportDataObj= null;
private IFetchData fetchDataObj= null;

//Set the fetch data object from outside of this class.
public void configureFetchData(IFetchData actualFetchDataObj){
this.fetchDataObj = actualFetchDataObj;
}
//Set the export data object from outside of this class.
public void configureExportData(IExportData actualExportDataObj){
this.exportDataObj = actualExportDataObj;
}

public Object generateBalanceSheet(){
List<Object[]> dataLst = fetchDataObj.fetchData();
return exportDataObj.exportData(dataLst);
}
}

Here are some quick observations:

  • Objects of fetch data and export data modules are created outside the balance sheet module, and passed through configureFetchData() and configureExportData() methods
  • The balance sheet module is now  100 percent decoupled from fetch data and export data modules
  • For any new type of fetch and export data, no change is required in balance sheet modules

At this moment, the relation between DIP and IoC can be described as per the following diagram:

Finally, we implemented DIP through IoC and solved one of the most fundamental problems of interdependency between modules.

But hold on, something is not complete yet. We have seen that keeping the object creation away from your main module will eliminate the risk of accommodating changes and make your code decoupled. But we haven't explored how to create and pass the dependency object from outside code into your module. There are various ways of inverting object creation.

Different ways to invert object creation

We have seen how inversion of object creation helps us to decouple the modules. You can achieve the inversion of object creation with multiple design patterns as follows:

  • Factory pattern
  • Service locator
  • Dependency injection

Inversion of object creation through the factory pattern

The factory pattern takes the responsibility of creating an object from a client who uses it. It generates the object of classes that are following a common interface. A client has to pass only type of the implementation it wants and the factory will create that object.

If we apply the factory pattern to our balance sheet example, the process of inverting of object creation is depicted as per the following diagram:

  • Client (in our case, it's a balance sheet module) talks to the factory—Hey factory, can you please give me the fetch data object? Here is the type.
  • The factory takes the type, creates the object, and passes it to the client (the balance sheet module).
  • The factory can create the object of the same type only.
  • The factory class is a complete black box for its clients. They know it's a static method to get objects.

The Balance Sheet module can get FetchData objects from FetchDataFactory. The code of FetchDataFactory will be as follows:

public class FetchDataFactory {
public static IFetchData getFetchData(String type){
IFetchData fetchData = null;
if("FROM_DB".equalsIgnoreCase(type)){
fetchData = new FetchDatabase();
}else if("FROM_WS".equalsIgnoreCase(type)){
fetchData = new FetchWebService();
}else {
return null;
}
return fetchData;
}
}

To use this factory, you need to update the configureFetchData() method of a balance sheet module as follows:

//Set the fetch data object from Factory.
public void configureFetchData(String type){
this.fetchDataObj = FetchDataFactory.getFetchData(type);
}

For export data, you need to create a separate factory as per the following snippet:

public class ExportDataFactory {

public static IExportData getExportData(String type){
IExportData exportData = null;
if("TO_HTML".equalsIgnoreCase(type)){
exportData = new ExportHTML();
}else if("TO_PDF".equalsIgnoreCase(type)){
exportData = new ExportPDF();
}else {
return null;
}
return exportData;
}
}

If a new fetch data or export data type is introduced, you need to change it in its respective factory class only.

Inversion of object creation through service locator

The service locator pattern works more or less the same as to the factory pattern. The service locator can find the existing object and send it to the client rather than create a new one every time, as with the factory pattern. Instead of getting into detail, we will just look briefly at how the service locator works to create objects. The flow of the service locator can be described as per the following diagram:

  • Client is relying on Service Locator to find services. Here, service means any kind of dependency
  • Service Locator takes the name of the service, and returns the object of service back to the client

If our balance sheet module uses the service locator, the code of the configureFetchData() method would be like the following snippet:

//Set the fetch data object from ServiceLocator.
public void configureFetchData(String type){
this.fetchDataObj = FetchDataServiceLocator.Instance.getFetchData(type);
}

Similar to fetch data, you need to design a separate service locator for export data. For any new fetch data or export data type, the changes need to be done in the service locator.

Another way of inverting the object creation is DI.

Dependency injection

 DI is one of the ways to invert the object creation process from your module to other code or entity. The term injection refers to the process of passing the dependent object into a software component.

Since DI is one of the ways to implement IoC, it relies on abstraction to set the dependency. The client object doesn't know which class will be used to provide functionality at compile time. The dependency will be resolved at runtime.

A dependent object does not directly call to the client object; instead, the client object will call a dependent object whenever required. It's similar to the Hollywood principle: Don't call us, we'll call you when we need to.

Dependency injection types

In DI, you need to set the entry point in a client object from which the dependency can be injected. Based on these entry points, DI can be implemented with the following types:

  • Constructor injection
  • Setter injection
  • Interface injection

Constructor injection

This is the most common way to inject dependency. In this approach, you need to pass the dependent object through a public constructor of a client object. Please note that in case of construction injection, you need to pass all the dependency objects in the constructor of a client object.

Constructor injection can control the order of instantiation and consequently reduce the risk of circular dependency. All mandatory dependencies can be passed through constructor injection.

In our BalanceSheet example, we need to pass two objects in a constructor, because it has two dependencies: one is for fetch data, and the second is for export data types, as per the following snippet:

public class BalanceSheet {

private IExportData exportDataObj= null;
private IFetchData fetchDataObj= null;

//All dependencies are injected from client's constructor
BalanceSheet(IFetchData fetchData, IExportData exportData){
this.fetchDataObj = fetchData;
this.exportDataObj = exportData;
}

public Object generateBalanceSheet(){
List<Object[]> dataLst = fetchDataObj.fetchData();
return exportDataObj.exportData(dataLst);
}
}

All dependencies are injected from a constructor of a client object. Since constructors are called only once, it's clear that the dependency object will not be changed until the existence of a client object. If a client uses constructor injection, then extending and overriding it would be difficult sometimes.

Setter injection

As its name suggests, here dependency injection is done through setter methods exposed publicly. Any dependency not required at the time of client object instantiation is called optional dependency. They can be set at a later stage after a client object is created.

Setter injection is a perfect fit for optional or conditional dependency. Let's apply a setter injection to the BalanceSheet module.

The code would look as follows:

public class BalanceSheet {

private IExportData exportDataObj= null;
private IFetchData fetchDataObj= null;

//Setter injection for Export Data
public void setExportDataObj(IExportData exportDataObj) {
this.exportDataObj = exportDataObj;
}

//Setter injection for Fetch Data
public void setFetchDataObj(IFetchData fetchDataObj) {
this.fetchDataObj = fetchDataObj;
}

public Object generateBalanceSheet(){

List<Object[]> dataLst = fetchDataObj.fetchData();
return exportDataObj.exportData(dataLst);
}

}

For each dependency, you need to put separate setter methods. Since the dependencies are set through the setter method, the object or a framework which supplies the dependencies need to call the setter methods at an appropriate time to make sure dependencies are available before a client object starts using it.

Interface injection

Interface injection defines a way by which the dependency provider should talk to a client. It abstracts the process of passing dependency. The dependency provider defines an interface that all clients need to implement. This method is not so frequently used.

Technically, interface injection and setter injection are the same. They both use some sort of method to inject dependency. However, for interface injection, the method is defined by objects which provide the dependency.

Let's apply interface injection to our balance sheet module:

public interface IFetchAndExport {
void setFetchData(IFetchData fetchData);
void setExportData(IExportData exportData);
}

//Client class implements interface
public class BalanceSheet implements IFetchAndExport {

private IExportData exportDataObj= null;
private IFetchData fetchDataObj= null;

//Implements the method of interface injection to set dependency
@Override
public void setFetchData(IFetchData fetchData) {
this.fetchDataObj = fetchData;
}

//Implements the method of interface injection to set dependency
@Override
public void setExportData(IExportData exportData) {
this.exportDataObj = exportData;

}

public Object generateBalanceSheet(){
List<Object[]> dataLst = fetchDataObj.fetchData();
return exportDataObj.exportData(dataLst);
}
}

We have created interface IFetchAndExport and defined methods to inject dependencies. The dependency provider class knows how to pass the dependency through this interface. Our client object (Balance Sheet module) implements this method to set dependencies.

IoC containers

So far, we have talked about the code or framework that plays the role of dependency provider. It can be any custom code or full-fledged IoC container. Some developers refer to it as a DI container, but we will simply call it a container.

If we write custom code to supply dependency, things get smoother until we have just a single level of dependency. Take the scenario where our client classes are also dependent of some other modules. This results in chained or nested dependencies.

In this situation, implementing dependency injection will become quite complicated through manual code. That is where we need to rely on containers. 

A container takes care of creating, configuring, and managing objects. You just need to do configuration, and the container will take care of object instantiation and dependency management with ease. You don't need to write any custom code such as that we wrote while implementing IoC with factory or service locator patterns.

So, as a developer, your life is cool. You just give a hint about your dependency, and the container will handle the rest and you can focus on implementing business logic.

If we choose containers to set dependencies for our Balance Sheet module, the container will create the objects of all dependencies first. Then, it will create an object of the Balance Sheet class and pass the dependencies in it. A container will do all these things silently and give you the object of the Balance Sheet module with all dependencies set in it. This process can be described with the following diagram:

In conclusion, the following are the advantages of using containers over manual code to manage dependency:

  • Isolating the process of object creation from your code and making your code more clean and readable.
  • Removing object wiring (setting dependency) code from your client module. The container will take care of object wiring.
  • Making your modules 100 percent loose coupling.
  • Managing the entire lifecycle of the modules. This is very helpful when you want to configure the objects for various scopes, such as request, session, and so on in application execution.
  • Swapping out the dependency is just a matter of configuration—no change is required in the code.
  • It is a more centralized way to handle object life span and dependency management. This is useful when you want to apply some common logic across the dependencies, for example, AOP in Spring. We will see details about AOP in Chapter 6Aspect-Oriented Programming and Interceptors.
  • Your module can benefit from advanced features that ship with containers.

Spring, Google Guice, and Dagger are some of the IoC containers available today for Java. Starting from Enterprise Edition version 6, Java introduced Context Dependency Injection (CDI), a dependency injection framework in Enterprise Edition. It's more or less similar to Spring's annotation-based DI implementation. Out of all the preceding containers, Spring is the most popular and widely used IoC container today.

Summary

In the software paradigm, it's always recommended to break the whole system down into small modules that can work independently for specific tasks. DIP is one of the important principles to build a modular system. In this chapter, we saw how high-level modules should not depend on low-level modules, and both should depend on abstraction (the concept of DIP).

We learned in detail how we can achieve DIP through IoC. Setting inversion of control makes a system loosely coupled. We also learned various design patterns such as factory, service locator, and dependency injection to implement IoC.

After that, we learned about the various types of the dependency injection pattern. Finally, we discussed IoC containers and how they're useful when building modular systems.

In the next chapter, we will talk about modularity concepts and dependency injection in Java 9.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • •Use DI to make your code loosely coupled to manage and test your applications easily on Spring 5 and Google Guice
  • •Learn the best practices and methodologies to implement DI
  • •Write more maintainable Java code by decoupling your objects from their implementations

Description

Dependency Injection (DI) is a design pattern that allows us to remove the hard-coded dependencies and make our application loosely coupled, extendable, and maintainable. We can implement DI to move the dependency resolution from compile-time to runtime. This book will be your one stop guide to write loosely coupled code using the latest features of Java 9 with frameworks such as Spring 5 and Google Guice. We begin by explaining what DI is and teaching you about IoC containers. Then you’ll learn about object compositions and their role in DI. You’ll find out how to build a modular application and learn how to use DI to focus your efforts on the business logic unique to your application and let the framework handle the infrastructure work to put it all together. Moving on, you’ll gain knowledge of Java 9’s new features and modular framework and how DI works in Java 9. Next, we’ll explore Spring and Guice, the popular frameworks for DI. You’ll see how to define injection keys and configure them at the framework-specific level. After that, you’ll find out about the different types of scopes available in both popular frameworks. You’ll see how to manage dependency of cross-cutting concerns while writing applications through aspect-oriented programming. Towards the end, you’ll learn to integrate any third-party library in your DI-enabled application and explore common pitfalls and recommendations to build a solid application with the help of best practices, patterns, and anti-patterns in DI.

Who is this book for?

This book is for Java developers who would like to implement DI in their application. Prior knowledge of the Spring and Guice frameworks and Java programming is assumed.

What you will learn

  • •Understand the benefits of DI and fo from a tightly coupled design to a cleaner design organized around dependencies
  • • See Java 9's new features and modular framework
  • •Set up Guice and Spring in an application so that it can be used for DI
  • •Write integration tests for DI applications
  • •Use scopes to handle complex application scenarios
  • •Integrate any third-party library in your DI-enabled application
  • •Implement Aspect-Oriented Programming to handle common cross-cutting concerns such as logging, authentication, and transactions
  • • Understand IoC patterns and anti-patterns in DI
Estimated delivery fee Deliver to Ireland

Premium delivery 7 - 10 business days

€23.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Apr 26, 2018
Length: 246 pages
Edition : 1st
Language : English
ISBN-13 : 9781788296250
Vendor :
Pivotal
Category :
Languages :
Tools :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Estimated delivery fee Deliver to Ireland

Premium delivery 7 - 10 business days

€23.95
(Includes tracking information)

Product Details

Publication date : Apr 26, 2018
Length: 246 pages
Edition : 1st
Language : English
ISBN-13 : 9781788296250
Vendor :
Pivotal
Category :
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
€18.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
€189.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts
€264.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 115.97
Design Patterns and Best Practices in Java
€36.99
Java 9 Dependency Injection
€36.99
Test-Driven Java Development, Second Edition
€41.99
Total 115.97 Stars icon

Table of Contents

8 Chapters
Why Dependency Injection? Chevron down icon Chevron up icon
Dependency Injection in Java 9 Chevron down icon Chevron up icon
Dependency Injection with Spring Chevron down icon Chevron up icon
Dependency Injection with Google Guice Chevron down icon Chevron up icon
Scopes Chevron down icon Chevron up icon
Aspect-Oriented Programming and Interceptors Chevron down icon Chevron up icon
IoC Patterns and Best Practices Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
(3 Ratings)
5 star 33.3%
4 star 33.3%
3 star 33.3%
2 star 0%
1 star 0%
Darshak Patel Jul 03, 2018
Full star icon Full star icon Full star icon Full star icon Full star icon 5
The Book is good. I was not sure at first but then I loved it. It nicely covers dependency injection concept. Kudos to the writers.
Amazon Verified review Amazon
Seattle Sleuth Sep 16, 2018
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
This is a very good book, and I'll explain the 4 instead of 5 in a moment.The examples are clean and make sense. Spring and Guice examples are good.This is the best Java ioc/di book I've seen.The best ioc/di book overall that I have ever read is Mark Seemann's Dependency Injection in .NET (I have first and second editions) and this .Net book takes you thru ~why you would pick Ioc/Di via an example. He takes a crappy fake monolith application and teaches you how/why to change it to a good ioc/di pattern. So the Mark Seemann book is the gold standard imho.But this Java book is very good. I'd buy it again in a heartbeat.To the author of this book. Well done. I would suggest second edition include a "why" chapter with a small before and after example application. And a chapter about centralized ioc which mark seemann calls the composite-root pattern.This Java book is a keeper. If you're a Java developer, don't be Java @rrogant and read this book .. but also read the mark seemann book.
Amazon Verified review Amazon
Chris Jul 31, 2024
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3
Good information, but many of the code snippets contain errors (for example, missing spaces) and sometimes the text contains errors or doesn't make sense (like nobody did a serious proofreading prior to publishing).
Subscriber review Packt
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela