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:

Rigidity – hard to change because of the impact on every other part of the system. Fragility – changes break other parts of the system. 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:

// DON'T DO THIS public class BadExample { public static void main(String[] args) { new ComputerProcessor() .addComputer(new Doubler()) .addComputer(new Squarer()) .computeAll(8); } static class ComputerProcessor { private List computers = new ArrayList(); public ComputerProcessor addComputer(Object o) { computers.add(o); return ComputerProcessor.this; } public void computeAll(long value) { for (Object o : computers) { long computedValue = -1; if (o instanceof Doubler) { computedValue = ((Doubler) o).computeDouble(value); } else if (o instanceof Squarer) { computedValue = ((Squarer) o).computeSquare(value); } String name = o.getClass().getSimpleName(); System.out.println("Computer: " + name + ", value: " + value + " computed value: " + computedValue); } } } static class Doubler { public long computeDouble(long value) { return value*2; } } static class Squarer { public long computeSquare(long value) { return value*value; } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 // DON'T DO THIS public class BadExample { public static void main ( String [ ] args ) { new ComputerProcessor ( ) . addComputer ( new Doubler ( ) ) . addComputer ( new Squarer ( ) ) . computeAll ( 8 ) ; } static class ComputerProcessor { private List computers = new ArrayList ( ) ; public ComputerProcessor addComputer ( Object o ) { computers . add ( o ) ; return ComputerProcessor . this ; } public void computeAll ( long value ) { for ( Object o : computers ) { long computedValue = - 1 ; if ( o instanceof Doubler ) { computedValue = ( ( Doubler ) o ) . computeDouble ( value ) ; } else if ( o instanceof Squarer ) { computedValue = ( ( Squarer ) o ) . computeSquare ( value ) ; } String name = o . getClass ( ) . getSimpleName ( ) ; System . out . println ( "Computer: " + name + ", value: " + value + " computed value: " + computedValue ) ; } } } static class Doubler { public long computeDouble ( long value ) { return value* 2 ; } } static class Squarer { public long computeSquare ( long value ) { return value* value ; } } }

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:

public class IoCExample { public static void main(String[] args) { new ComputerProcessor() .addComputer(new Doubler()) .addComputer(new Squarer()) .computeAll(8); } static class ComputerProcessor { private List<Computer> computers = new ArrayList<>(); public ComputerProcessor addComputer(Computer c) { computers.add(c); return ComputerProcessor.this; } public void computeAll(long value) { for (Computer c : computers) { String name = c.getClass().getSimpleName(); System.out.println("Computer: " + name + ", value: " + value + " computed value: " + c.compute(value)); } } } interface Computer { long compute(long value); } static class Doubler implements Computer { public long compute(long value) { return value*2; } } static class Squarer implements Computer { public long compute(long value) { return value*value; } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class IoCExample { public static void main ( String [ ] args ) { new ComputerProcessor ( ) . addComputer ( new Doubler ( ) ) . addComputer ( new Squarer ( ) ) . computeAll ( 8 ) ; } static class ComputerProcessor { private List < Computer > computers = new ArrayList < > ( ) ; public ComputerProcessor addComputer ( Computer c ) { computers . add ( c ) ; return ComputerProcessor . this ; } public void computeAll ( long value ) { for ( Computer c : computers ) { String name = c . getClass ( ) . getSimpleName ( ) ; System . out . println ( "Computer: " + name + ", value: " + value + " computed value: " + c . compute ( value ) ) ; } } } interface Computer { long compute ( long value ) ; } static class Doubler implements Computer { public long compute ( long value ) { return value* 2 ; } } static class Squarer implements Computer { public long compute ( long value ) { return value* value ; } } }

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:

@RestController public class HomeController { @Autowired GreetingService greetingService; @RequestMapping("/") public String home() { return greetingService.greet(); } } 1 2 3 4 5 6 7 8 9 10 11 12 @ RestController public class HomeController { @ Autowired GreetingService greetingService ; @ RequestMapping ( "/" ) public String home ( ) { return greetingService . greet ( ) ; } }

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 :

public interface GreetingService { String greet(); } 1 2 3 4 5 public interface GreetingService { String greet ( ) ; }

Pretty straightforward. Here’s an implementation class:

@Service public class EnglishGreetingService implements GreetingService { @Override public String greet() { return "Hello World!"; } } 1 2 3 4 5 6 7 8 9 @ Service public class EnglishGreetingService implements GreetingService { @ Override public String greet ( ) { return "Hello World!" ; } }

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 :

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 :

@Service public class FrenchGreetingService implements GreetingService { @Override public String greet() { return "Bonjour Monde!"; } } 1 2 3 4 5 6 7 8 9 @ Service public class FrenchGreetingService implements GreetingService { @ Override public String greet ( ) { return "Bonjour Monde!" ; } }

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

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.stormpath.example.service.GreetingService] is defined: expected single matching bean but found 2: englishGreetingService,frenchGreetingService 1 2 Caused by : org . springframework . beans . factory . NoUniqueBeanDefinitionException : No qualifying bean of type [ com . stormpath . example . service . GreetingService ] is defined : expected single matching bean but found 2 : englishGreetingService , frenchGreetingService

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.

@Configuration public class GreetingServiceConfig { @Bean @ConditionalOnProperty(name = "language.name", havingValue = "english", matchIfMissing = true) public GreetingService englishGreetingService() { return new EnglishGreetingService(); } @Bean @ConditionalOnProperty(name = "language.name", havingValue = "french") public GreetingService frenchGreetingService() { return new FrenchGreetingService(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @ Configuration public class GreetingServiceConfig { @ Bean @ ConditionalOnProperty ( name = "language.name" , havingValue = "english" , matchIfMissing = true ) public GreetingService englishGreetingService ( ) { return new EnglishGreetingService ( ) ; } @ Bean @ ConditionalOnProperty ( name = "language.name" , havingValue = "french" ) public GreetingService frenchGreetingService ( ) { return new FrenchGreetingService ( ) ; } }

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

java -jar target/*.jar & http localhost:8080 1 2 3 java - jar target /* . jar & http localhost : 8080

Also English

LANGUAGE_NAME=english java -jar target/*.jar & http localhost:8080 1 2 3 LANGUAGE_NAME = english java - jar target /* . jar & http localhost : 8080

French

LANGUAGE_NAME=french java -jar target/*.jar & http localhost:8080 1 2 3 LANGUAGE_NAME = french java - jar target /* . jar & http localhost : 8080

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:

public class MeaningOfLife { final int lifeInt; final String lifeString; public MeaningOfLife(int lifeInt, String lifeString) { this.lifeInt = lifeInt; this.lifeString = lifeString; } public int getLifeInt() { return lifeInt; } public String getLifeString() { return lifeString; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MeaningOfLife { final int lifeInt ; final String lifeString ; public MeaningOfLife ( int lifeInt , String lifeString ) { this . lifeInt = lifeInt ; this . lifeString = lifeString ; } public int getLifeInt ( ) { return lifeInt ; } public String getLifeString ( ) { return lifeString ; } }

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:

@Component public class MeaningOfLife { final int lifeInt; final String lifeString; @Autowired public MeaningOfLife( @Value("#{ @environment['life.int'] ?: 0 }") int lifeInt, @Value("#{ @environment['life.string'] ?: '0' }") String lifeString ) { this.lifeInt = lifeInt; this.lifeString = lifeString; } public int getLifeInt() { return lifeInt; } public String getLifeString() { return lifeString; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @ Component public class MeaningOfLife { final int lifeInt ; final String lifeString ; @ Autowired public MeaningOfLife ( @ Value ( "#{ @environment['life.int'] ?: 0 }" ) int lifeInt , @ Value ( "#{ @environment['life.string'] ?: '0' }" ) String lifeString ) { this . lifeInt = lifeInt ; this . lifeString = lifeString ; } public int getLifeInt ( ) { return lifeInt ; } public String getLifeString ( ) { return lifeString ; } }

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:

Set the value of intLife to 0 unless Spring can load a property named life.int from application.properties or an environment variable named LIFE_INT 1 2 Set the value of intLife to 0 unless Spring can load a property named life . int from application . properties or an environment variable named LIFE_INT

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

... @Autowired MeaningOfLife meaningOfLife; ... @RequestMapping("meaningOfLife") public String meaningOfLife() { return meaningOfLife.getLifeString(); } ... 1 2 3 4 5 6 7 8 9 10 11 . . . @ Autowired MeaningOfLife meaningOfLife ; . . . @ RequestMapping ( "meaningOfLife" ) public String meaningOfLife ( ) { return meaningOfLife . getLifeString ( ) ; } . . .

If you launch the app like so:

mvn clean install LIFE_STRING=42 java -jar target/*.jar & 1 2 3 mvn clean install LIFE_STRING = 42 java - jar target /* . jar &

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:

public interface Nameable { public String getName(); } 1 2 3 4 5 public interface Nameable { public String getName ( ) ; }

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

@Component public class Dog implements Nameable { @Override public String getName() { return "Fluffy"; } } 1 2 3 4 5 6 7 8 9 @ Component public class Dog implements Nameable { @ Override public String getName ( ) { return "Fluffy" ; } }

and

@Component public class Person implements Nameable { @Override public String getName() { return "Micah"; } } 1 2 3 4 5 6 7 8 9 @ Component public class Person implements Nameable { @ Override public String getName ( ) { return "Micah" ; } }

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

@Component public class NameHelper { private Nameable person; private Nameable dog; public String getName(String type) { switch (type) { case "person": return person.getName(); case "dog": return dog.getName(); default: return "UNKNOWN"; } } @Autowired @Qualifier("person") public void setPerson(Nameable person) { this.person = person; } @Autowired @Qualifier("dog") public void setDog(Nameable dog) { this.dog = dog; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @ Component public class NameHelper { private Nameable person ; private Nameable dog ; public String getName ( String type ) { switch ( type ) { case "person" : return person . getName ( ) ; case "dog" : return dog . getName ( ) ; default : return "UNKNOWN" ; } } @ Autowired @ Qualifier ( "person" ) public void setPerson ( Nameable person ) { this . person = person ; } @ Autowired @ Qualifier ( "dog" ) public void setDog ( Nameable dog ) { this . dog = dog ; } }

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:

... @Autowired NameHelper nameHelper; ... @RequestMapping("/name") public String name(@RequestParam String type) { return nameHelper.getName(type); } ... 1 2 3 4 5 6 7 8 9 10 11 . . . @ Autowired NameHelper nameHelper ; . . . @ RequestMapping ( "/name" ) public String name ( @ RequestParam String type ) { return nameHelper . getName ( type ) ; } . . .

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

http localhost:8080/name?type=person 1 2 http localhost : 8080 / name ? type = person

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:

... private Nameable[] nameables; ... public String[] getAllNames() { return Arrays.stream(nameables).map((Nameable::getName)).toArray(String[]::new); } ... @Autowired public void setNameables(Nameable[] nameables) { this.nameables = nameables; } ... 1 2 3 4 5 6 7 8 9 10 11 12 13 . . . private Nameable [ ] nameables ; . . . public String [ ] getAllNames ( ) { return Arrays . stream ( nameables ) . map ( ( Nameable : : getName ) ) . toArray ( String [ ] : : new ) ; } . . . @ Autowired public void setNameables ( Nameable [ ] nameables ) { this . nameables = nameables ; } . . .

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 Nameable s in the array.

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

... @Autowired NameHelper nameHelper; ... @RequestMapping("/allNames") public String[] name() { return nameHelper.getAllNames(); } ... 1 2 3 4 5 6 7 8 9 10 . . . @ Autowired NameHelper nameHelper ; . . . @ RequestMapping ( "/allNames" ) public String [ ] name ( ) { return nameHelper . getAllNames ( ) ; } . . .

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

HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Date: Wed, 27 Jul 2016 21:36:20 GMT Server: Apache-Coyote/1.1 Transfer-Encoding: chunked [ "Fluffy", "Micah" ] 1 2 3 4 5 6 7 8 9 10 11 HTTP / 1.1 200 OK Content - Type : application / json ; charset = UTF - 8 Date : Wed , 27 Jul 2016 21 : 36 : 20 GMT Server : Apache - Coyote / 1.1 Transfer - Encoding : chunked [ "Fluffy" , "Micah" ]

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.

... @Autowired ApplicationContext appContext; ... @RequestMapping("/beans") public Map<String, String[]> beans(@RequestParam(required = false) String q) { Map<String, String[]> retMap = new HashMap<>(); String[] retArray = Arrays.stream(appContext.getBeanDefinitionNames()) .filter(beanName -> (q == null || q.length() == 0) || beanName.toLowerCase().contains(q.trim().toLowerCase()) ) .toArray(String[]::new); retMap.put("beans", retArray); return retMap; } ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 . . . @ Autowired ApplicationContext appContext ; . . . @ RequestMapping ( "/beans" ) public Map < String , String [ ] > beans ( @ RequestParam ( required = false ) String q ) { Map < String , String [ ] > retMap = new HashMap < > ( ) ; String [ ] retArray = Arrays . stream ( appContext . getBeanDefinitionNames ( ) ) . filter ( beanName - > ( q == null | | q . length ( ) == 0 ) | | beanName . toLowerCase ( ) . contains ( q . trim ( ) . toLowerCase ( ) ) ) . toArray ( String [ ] : : new ) ; retMap . put ( "beans" , retArray ) ; return retMap ; } . . .

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

HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Date: Wed, 27 Jul 2016 21:42:21 GMT Server: Apache-Coyote/1.1 Transfer-Encoding: chunked { "beans": [ "greetingServiceConfig", "englishGreetingService" ] } 1 2 3 4 5 6 7 8 9 10 11 12 13 HTTP / 1.1 200 OK Content - Type : application / json ; charset = UTF - 8 Date : Wed , 27 Jul 2016 21 : 42 : 21 GMT Server : Apache - Coyote / 1.1 Transfer - Encoding : chunked { "beans" : [ "greetingServiceConfig" , "englishGreetingService" ] }

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].