Evaluate Weigh the pros and cons of technologies, products and projects you are considering.

IoC example in Spring: Inversion of control explained clearly and quickly

Many developers struggle with the inversion of control principle. This simple IoC example will bring clarity to the popular design pattern. Every enterprise software developer needs a strong understanding of IoC, as it is used extensively in Jakarta EE, Spring Boot and even JavaScript frameworks, such as Angular and Ember.

Inversion of control in Spring

For this IoC example, we will first look at a piece of Java code that's IoC-free. We will then integrate Spring functionality and invert control so that the Spring IoC container -- not the developer's code -- provisions Java instances. This example will demonstrate IoC in Spring, but the concepts here are universal and apply equally in other frameworks.

The IoC example

I often use the Rock-Paper-Scissors game to demonstrate programming concepts. This example will use a GameService class, along with a class named Score, which keeps track of the number of wins, losses and ties. Here are the two classes, with the GameService class holding a reference to the Score.

public class Score {
   int wins, losses, ties;

 
public class GameService {
   Score score = new Score();
}

As it stands, this code does not use IoC.

The GameService class uses an instance of the Score class, and it provisions itself an instance directly in the code with the new keyword and the Score() constructor. The developer now controls the creation and provisioning of object instances. But we will soon change that flow as we introduce the Spring IoC container into the mix.

To incorporate Spring support into the application, add a Spring Framework dependency to the project's Maven POM file:

POM file
Add the Spring IoC container dependency to the Maven POM file.

The Spring IoC container

To incorporate Spring into this IoC example, you need an XML-based configuration file to inform the IoC container of the various JavaBeans it will be managing. Spring bean definitions can sometimes be extensive, especially when it comes to the configuration of resources like message queues or database connections. In this example, the configuration will be minimal. The goal here is to simply inform the Spring container that:

  • It needs to manage an instance of the class Score.
  • Programs will refer to this instance using thescore as the name.

These details are written into a file named spring-context.xml and saved in the resources folder of the application. There is a significant preamble to the XML file, with defined schemas and namespaces, but that is all boilerplate text that appears at the start of every Spring configuration file. The content of note is the definition of the bean with the ID of thescore.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
      <bean id="thescore" class="com.mcnz.ioc.example.Score"/>   
 
</beans>

When this application starts, the Spring IoC container consumes the spring-context.xml file once initialized. In Java code, the Spring IoC container manifests itself as an instance of the ApplicationContext class and is often initialized through an instance of the ClassPathXmlApplicationContext as so:

ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");

Inversion of control explained

With the Spring IoC container initialized, the GameService can invert control of the creation of the Score class, gaining access to the instance through the Spring ApplicationContext instance instead of making a direct call to the constructor of the Score class. This is how the concept of inversion of control manifests itself in the Spring framework.

//Score score = new Score();
Score score = context.getBean("thescore", Score.class);

Notice that the getBean() method gets a String named thescore. That is because thescore is the name used to map to the Score class in the spring-context.xml file.

<bean id="thescore" class="com.mcnz.ioc.example.Score"/>

It's easy to assume that dependency injection and IoC always happen in tandem, but that is simply not the case.

The final result is that the IoC container creates and provisions the Score class. This unburdens the software developer. The container now shoulders the responsibility of control, thus the term inversion of control.

Note that this IoC example does not use dependency injection. It's easy to assume that dependency injection and IoC are always happening in tandem, but that is simply not the case. In this example, control is inverted without any injected dependencies.

Completion of the IoC example

To polish off the example, we could implement a bit of logic in the GameService class that actually plays a quick rendition of the game.

package com.mcnz.ioc.example;
 
import org.springframework.context.*;
import org.springframework.context.support.*;
/* Inversion of control example class */
public class GameService {
    
     ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); 
     Score score = context.getBean("thescore", Score.class);
 
     public void playTheGame(String clientGesture) {
           if (clientGesture.equals("scissors")) { score.wins++; }
           if (clientGesture.equals("rock"))       { score.ties++; }

           if (clientGesture.equals("rock"))       { score.losses++; }
     }
}
/* End of Inversion of control example class */

With the GameService class completed, add a class with an executable main method -- named SpringIocExample -- to test the IoC example.

package com.mcnz.ioc.example;
 
public class SpringIocExample {
 
     public static void main(String args[]) {
           GameService gs = new GameService();
           gs.playTheGame("scissors");
           gs.playTheGame("paper");
           gs.playTheGame("scissors");
           gs.playTheGame("paper");
 
           System.out.print("Wins: " + gs.score.wins);
           System.out.print("..Losses: " + gs.score.losses);
           System.out.print("..Ties: " + gs.score.ties);
 
     }
}

With all of the required classes and XML files created, the structure of the Eclipse project looks as follows:

project structure
Note the Eclipse project structure of the inversion of control example.

When the SpringIocExample runs, inversion of control is successful, and the output is:

Wins: 2.. Losses: 2..Ties: 0

Inversion of control benefits

And that is how easy it is to implement IoC with the Spring Framework. While this IoC example used a basic class as the example, you can feel the real power of IoC when you need to access objects with complex configurations, such as persistent data stores or content management systems. IoC is an incredibly useful design pattern, and it's no wonder it is a core feature of so many enterprise development frameworks.

You can find the source code for this inversion of control example on GitHub.

View All Videos
App Architecture
Software Quality
Cloud Computing
Security
SearchAWS
Close