7

Notes: Inversion of Control and Dependency Injection with Spring - Part 1

 3 years ago
source link: https://alcher.dev/2018/notes-ioc-di-spring-1/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Dec 26, 2018 · John Alcher

archived, spring, java

Notes: Inversion of Control and Dependency Injection with Spring - Part 1

This is an archived post from my previous blog. Some content may be outdated. FYI!


Introduction

Recently, I’ve been trying to get into Web development with Java just to get a breath of fresh air from my usual PHP and Laravel work. So I picked up Spring and started learning some of its core components. I thought that having a good understanding of the core components is a good idea before diving in with Web specific details. I started with Spring’s Inversion of Control (IoC) / Dependency Injection (DI) container because it appears that the other Spring components heavily use the container itself.

This is a 2 part series in where I discuss the usage of IoC / DI within a Java application and how to use Spring’s IoC / DI container to simplify the creation and wiring up of objects within a system.

Terminologies

If you are a Ruby, Python, or JavaScript developer, the concept of IoC / DI might come as weird and foreign. On the other hand, if you’re a C#, Java, or PHP developer, there’s a good chance that you’re already using IoC / DI without even knowing it. Here’s a definition from Wikipedia:

Inversion of Control

In software engineering, inversion of control (IoC) is a design principle in which custom-written portions of a computer program receive the flow of control from a generic framework. A software architecture with this design inverts control as compared to traditional procedural programming: in traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks, but with inversion of control, it is the framework that calls into the custom, or task-specific, code.

Dependency Injection

In software engineering, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.

What?

Yeah, that’s a mouthful. On a very simple level, I’ll try to define what IoC and DI with code samples:

// ACME Web Services run a weekly newsletter that sends e-mail offerings
// to its subscribers.
// 
// The Newsletter class is responsible for generating the e-mail contents, fetching
// the subscribers, and other related procedures. The act of sending the e-mails itself
// is handled by a BulkMailer class, which sends the e-mails in ""bulk"".
public class Newsletter {
    private BulkMailer mailer = new BulkMailer();

    public void send() {
        mailer.send();
    }
}

A few months later, the managers at ACME decided to send newsletters with respect to the subscribers preferences. So they developed a new class, PreferenceMailer which does just that. They replaced the mailer field to be an instance of PreferenceMailer and all works just fine.

// Single line change, no big deal. Or is it?
public class Newsletter {
    private PreferenceMailer mailer = new PreferenceMailer();

    // ...
}

The problem here is that down the road when change is required, the mailer field will be changed again and again. What if we need to change multiple fields across multiple files? This doesn’t scale well. With our current setup, the Newsletter class controls the mailing service that it uses. A solution is to invert that control, and let the client provide the implementation that they want, thus decoupling the Newsletter class from whatever mailing service it might use.

To do this, we need to extract an interface out of the mailing services:

public interface Mailer {
    public void send();
}

And then use that interface as the contract in which the mailing service adheres:

public class Newsletter {
    private Mailer mailer;
    
    public void send() {
        // It doesn't matter how the mailer is implemented because we
        // can be sure that it has a send() method.
        mailer.send();
    }
}

Now, mailer can be BulkMailer or PreferenceMailer or whatever implementations we might get down the road, but it will still work the same without any code change for the Newsletter class. But how do we instantiate a mailer class now? We removed the new keyword on the mailer field, so we’re stuck with a null at this point. Here’s where Dependency Injection comes in:

public class Newsletter {
    // The Newsletter class needs a mailer to work, hence it
    // it is a ""dependency""
    private Mailer mailer;
    
    // We ""inject"" that dependency through the constructor.
    public Newsletter(Mailer mailer) {
        this.mailer = mailer;
    }
    
    public void send() {
        mailer.send();
    }
}

Now, the Newsletter class is fully decoupled from the implementation of its mailer dependency. and any type of mailing service, as long as it implements the Mailer interface, can be freely swapped in and out through the constructor injection. Neat!

Conclusion

We now have a basic understanding of Inversion of Control and Dependency Injeciton. With part 2, we’ll explore some code examples that utilizes the Spring IoC / DI container, and see how it simplifies the creation and wiring up of objects within our system.


Got any feedback or suggestions? Feel free to send me an email or a tweet.
Ciao!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK