Welcome to the second post in our Spring Boot Technical Concepts series! Today we’ll dive deep into Dependency Injection with Spring Boot. If you want to start at the beginning, be sure to check out Default Starters – Spring Boot Technical Concepts Series, Part 1. But now, let’s talk Dependency Injection!

There was a great convergence of ideas and technology right around 1995. That was when when Bob Martin (fondly called Uncle Bob) first started talking about the “commandments” of Object Oriented Design. It’s also when Java – a then new Object Oriented programming language – was first released.

By the year 2000, Bob Martin had firmly codified his Design Principles and Design Patterns for Object Oriented Development. Somewhere in the early 2000s, Michael Feathers coined the mnemonic SOLID to describe the first 5 principles of OOD – in particular, class design. Each letter also has a 3-letter representation.

 S   SRP   Single Responsibility Principle 
 O   OCP   Open Closed Principle 
 L   LSP   Liskov Substitution Principle 
 I   ISP   Interface Segregation Principle 
 D   DIP   Dependency Inversion Principle 

In this post, we are focusing on the “D” – Dependency Inversion Principle. Uncle Bob tells us: “Depend on abstractions, not on concretions.” This is sometimes referred to as Inversion of Control. In 2004, Martin Fowler referred to it as Dependency Injection.

What’s Dependency Injection?

Let’s take a look at a definition of Inversion of Control. Uncle Bob Martin says there are three aspects of a bad design:

  1. Rigidity – hard to change because of the impact on every other part of the system.
  2. Fragility – changes break other parts of the system.
  3. Immobility – reuse elsewhere is hard because of entanglement between components of the system

Inversion of Control addresses these three issues using Uncle Bob’s simple approach: Depend on abstractions, not on concretions. In the world of Java, that means relying on interfaces and not implementations.

The World Before Dependency Injection

Let’s take a look at a counter example:

The ComputerProcessor is very fragile in this example. If we add a new class called Cuber, and we want to be able to represent it in ComputerProcessor, we need another if statement and more casting in order to call its computeCube method.

Here’s the same example using Inversion of Control:

The enabling technology here is the Computer interface. Now, ComputerProcessor only deals with objects that conform to the Computer interface. If we want to add a new Cuber class, all it has to do is implement the Computer interface. Nothing need change in ComputerProcessor to deal with Cuber objects.

Dependency Injection takes this concept and systematizes it in a way that makes it even easier to interact with your interfaces. Throughout this post, I will be using a Spring Boot example to demonstrate Dependency Injection. Since 2003, Dependency Injection has been a core feature of the Spring framework. The source code can be found here.

Dependency Injection, Spring Style

Let’s jump into a simple example and then break it down:

Spring introduced the @Autowired annotation for dependency injection. Any of the Spring components can be autowired. These include, components, configurations, services and beans. We’ll look at a few of these in detail below.

It’s a common pattern for controllers to be responsible for managing requests and responses while services perform business logic. Let’s look at our GreetingService:

Pretty straightforward. Here’s an implementation class:

The @Service annotation makes it autowireable. Spring injects the dependency into our controller. If I set a breakpoint, I can see the implementation class backing greetingService:

EnglishGreetingService

Together, this setup adheres to the Dependency Inversion Principle. If the internals of the implementation class change, we don’t need to touch HomeController.

Now, let’s say I want to support another GreetingService implementation. Say, FrenchGreetingService:

If I try to fire up my Spring Boot app now, I will get an error:

Spring Boot has no way of knowing which one of my implementation classes I want to inject into the HomeController. However, it does have a number of powerful annotations to deal with this. Let’s say that I want to inject the language specific version of the greeting service based on a property setting. We can use a Configuration to tell Spring Boot which Service we want.

On line 1, we are telling Spring Boot that this is a @Configuration. That causes this class to be instantiated and the beans defined within to (potentially) be exposed for use throughout the Spring ecosystem. I’ve also removed the @Service annotation from the EnglishGreetingService and FrenchGreetingService classes as it’s now this @Configuration‘s responsibility to instantiate and expose the services as beans.

The @Bean tells Spring Boot to expose a GreetingService (this is how it is able to be autowired into our HomeController). But, we are defining both beans with the same return type. That doesn’t seem right. The @ConditionalOnProperty annotation ensures that only one of these beans will be injected. It’s looking for the language.name property. Notice that there’s a matchIfMissing parameter in the case of the EnglishGreetingService. By default, that property is false. By setting it to true here, we are indicating to Spring Boot that if it doesn’t find a language.name property, that EnglishGreetingService is the default.

Let’s look at this in action. One thing to note is that Spring Boot automatically converts environment variables that are in all-caps with underscores to lowercase, dotted properties. Also, the examples below use HTTPie (https://github.com/jkbrzt/httpie), an alternative to curl. Curl will work as well. Try executing the following after doing mvn clean install from your command line:

English

Also English

French

Important Note: Spring has very deep support for internationalization that the above example is not taking advantage of. The example is meant for demonstration purposes only.

Well, we jumped right into the deep end of Dependency Injection (DI) with Spring. Let’s take a step back and look at the different types of DI supported by Spring.

DI Types

There are two basic types of Dependency Injection: constructor and setter. Spring recommends constructor based dependency in most cases. From the Spring docs: “Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.”

Constructor DI

Let’s say I have this POJO:

Notice that the two properties are final so they cannot be changed after instantiation. I can expose this class as a Spring managed bean by putting the @Component annotation at the top. If that’s all I did, we’d get an error because Spring would not know how to instantiate a MeaningOfLife object.

To address this, we can put the @Autowired annotation before the constructor and give Spring hints as to what the values of the constructor parameters should be. Here’s how the class looks now:

Here, we are using the @Value annotation to pick up properties from the environment and, importantly, we are providing defaults. This construct: @Value("#{ @environment['life.int'] ?: 0 }") int intLife can be read as:

By virtue of it being a component, we can now autowire MeaningOfLife elsewhere in our app, such as our HomeController:

If you launch the app like so:

and then hit our meaning of life endpoint: http localhost:8080/meaningOfLife, you will see it returns 42.

Setter DI

Let’s say I have this interface:

And I have a couple of implementation classes that I’ll expose as beans:

and

Let’s look at another component that will inject Person and Dog as setters:

Notice that both of our setters take a Nameable parameter. In order have Spring call these setters properly when it instantiates the NameHelper, we have to give it a hint to know which implementation to inject. In this case, we use the @Qualifier annotation with the bean name we want to inject. There’s a little bit of Spring magic going on here in that a bean’s default name will be it’s class name converted into standard Java variable format. So, it the class was named MyVeryImportantBean, it’s default name in Spring would be myVeryImportantBean.

In our HomeController, I’ve added a new method:

By default a @RequestParam is required, so in order to hit this endpoint, you must give it a type query parameter:

The above will respond with Micah.

Among the cool features of Spring is that you can autowire an array of bean interface type and Spring will load it up with all of the concrete objects its instantiated of that type. For instance, I’ve added the following to our NameHelper component:

The @Autowired setter will be called by Spring with an array containing a Person and a Dog Nameable. If we added another class that implements Nameable, it would automatically be added to this list.

The getAllNames method uses some of the nice Java 8 streams interface to give us an array of the calls to the getName method for each of the Nameables in the array.

In our HomeController we can add an endpoint to exercise this:

Hitting this endpoint, http localhost:8080/allNames returns:

Bringing it Home

It’s easy to get a list of all of the beans that are loaded into the Spring application context. These beans can be referenced and injected into other components of your application.

I’ve added a /beans endpoint to our HomeController to drive this point home, so to speak.

In this case, it takes an options query parameter: q. If you don’t provide the parameter, it will return all the beans Spring has available.

Try: http localhost:8080/beans

It’s a long list. Let’s look for some of the beans we created in this example:

`http localhost:8080/beans?q=greeting

Notice that there’s no frenchGreetingService. That’s because of our @ConditionalOnProperty setting in GreetingServiceConfig.

We’ve covered a lot of ground in this post. We looked at the history of Dependency Injection, its use in Spring services and components, as well as different approaches including constructor and setter dependency injection.

There’s an extensive section on Dependency Injection in the official Spring documentation here.

If you have any questions, you can tweet me at @afitnerd or send an email to [email protected].