spring

IoC in Spring Example

In this post, we will explain the IoC (Inversion of Control) and how it is applied in Spring.

1. Introduction

Inversion of control is an OOP design principle for writing code that is loosely coupled. The objective is to outsource certain responsibilities typically performed by a class to an external entity, such as a framework or container. The result is a software application that is configurable, modular, and therefore more easily maintained.

First, we are going to understand one of the patterns used to implements inversion of control: the Dependency Injection (DI).

1.1 Tools Used in this Example

  • Eclipse IDE for Enterprise Java Developers Version: 2019-09 R (4.13.0)
  • Spring Tools 4 – for Spring Boot 

Spring Tools 4 for Spring Boot is a set of plugins for Eclipse that support building and running Spring Boot applications. You can add Spring Tools 4 to your existing Eclipse installation by going to the Eclipse Marketplace and searching for “Spring Tools”.

2. Dependency Injection

Dependency Injection allows the creation of dependent objects outside of a class and provides those objects to a class with the help of an assembler. We usually speak of IoC as it relates to the creation and management of objects. In Spring, this task is delegated to the IoC container. You can learn more in our Dependency Injection Java Example.

The Spring IoC container uses information provided by configuration metadata to instantiate, build, and manage objects. Configuration metadata is provided through one or more of the following sources:

  • XML files
  • Java configuration classes
  • Java annotations

In the next sections, we will give an example of each approach.

3. IoC in Spring Example

3.1 Create the Spring Boot Project

We will be using Spring Boot for this example. In the New Project – Select a Wizard dialog box, expand Spring Boot, and select Spring Starter Project. Click “Next”.

IoC Spring - New Project
New Project

In the New Spring Starter Project dialog box, enter a name for the project. Also, enter the group, artifact, and package information. Accept all the other default values.

IoC Spring - New Spring Starter Project
New Spring Starter Project

In the New Spring Starter Project Dependencies dialog window, click “Finish”. (We will not require any dependencies for this example.)

IoC Spring - Project Dependencies
Project Dependencies

3.2 Define the Interface and Classes

For this example, we will use a simple interface and two implementations. Create a new package com.javacodegeeks.examples.beans and interface Animal with following code:

Animal.java

public interface Animal {

	String getFavoritePastime();
}

Also, create two classes (Dog and Cat) that implement the Animal interface.

Dog.java

public class Dog implements Animal {

	@Override
	public String getFavoritePastime() {
		return "I like to bark!";
	}
}

Cat.java

public class Cat implements Animal {

	@Override
	public String getFavoritePastime() {
		return "I like to nap!";
	}
}

3.3 Application without IoC

Modify the IocSpringApplication class in the com.javacodegeeks.examples package and add the following code:

IocSpringApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.javacodegeeks.examples.beans.Animal;
import com.javacodegeeks.examples.beans.Dog;

@SpringBootApplication
public class IocSpringApplication {

	public static void main(String[] args) {
		SpringApplication.run(IocSpringApplication.class, args);
		
		Animal animal = new Dog();
		
		System.out.println(animal.getFavoritePastime());
	}

}

The class is creating an instance of Dog using its constructor and the new keyword. It has taken on the responsibility of managing its own objects. This approach leads to a tightly coupled class.

But what if we want to change the implementation of the Animal type to Cat? As it stands now, we will need to modify the class, since it controls the construction of the concrete class (Dog or Cat).

To make this code loosely coupled, we will have the framework control which implementation is to be used. This is an Inversion of Control.

How does the framework decide which implementation to use? We need to configure the IoC container using metadata to assist in this regard. Let’s see how this can be accomplished.

3.4 XML Configuration

We can configure the Spring IoC container using XML files. Create an XML file in the src/main/resources directory and name it applicationContext.xml. (This is the naming convention, but you can name it anything you like.) This file will contain our configuration metadata. Add the following content to the file:

application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

	
    <!-- Define your beans here -->
    
	<bean id="myAnimal" class="com.javacodegeeks.examples.beans.Dog" />

</beans>

The <beans> tag contains the required namespaces used for Spring IoC configuration.

You define your beans between the <beans> tags. The <bean> tag in this example has two attributes:

  • id – The name to use when referencing this bean
  • class – the fully qualified name of the concrete class for this bean

We are defining one bean (Dog) in this file. This bean will be created and managed by the inversion of the control spring container instead of our class.

We can have one or more XML files that contain bean definitions. We specify the location of these resources using the @ImportResource annotation. Beans defined in the specified resource(s) will be registered in the IoC container when it is initialized. Add the @ImportResource annotation to the IocSpringApplication class as follows:

IocSpringApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

import com.javacodegeeks.examples.beans.Animal;
import com.javacodegeeks.examples.beans.Dog;

@SpringBootApplication
@ImportResource("classpath:/applicationContext.xml")
public class IocSpringApplication {

// some code...

}

In order to use our bean, we need to retrieve it from the ApplicatonContext. The static SpringApplication.run method returns the running ApplicationContext. Note the if we were using plain vanilla Spring instead of Spring Boot, we would need to instantiate the ApplicationContext ourselves, e.g. ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {" applicationContext.xml "}); .

Modify the the main method as follows:

IocSpringApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ImportResource;

import com.javacodegeeks.examples.beans.Animal;

@SpringBootApplication
@ImportResource("classpath:/applicationContext.xml")
public class IocSpringApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(
			IocSpringApplication.class, args);
		
		Animal animal = context.getBean("myAnimal", Animal.class);
		
		System.out.println(animal.getFavoritePastime());
		
		context.close();
	}

}

The interface org.springframework.context.ApplicationContext represents the Spring IoC container. (For the remainder of this article, the terms IoC container and application context will be used interchangeably.) ConfigurableApplicationContext is a subinterface of ApplicationContext and provides additional methods for configuring the IoC container. It also extends the Closeable interface, allowing us to close the application context, releasing any resources and destroying all cached singleton beans.

Now that we have a handle on the application context, we can retrieve the bean from the container using its bean id. We are using the overloaded ApplicationContext::getBean method that accepts the value of the bean id and the Java type as parameters to do this. By providing the type, we do not have to cast the return value, which provides a degree of type safety.

Save the change.  Right-click your project in Project Explorer and select Run As > Spring Boot App. You will see the following output in the console:

I like to bark!

If we want to use a different implementation for our Animal interface, we do not have to modify the class. We can change the implementation by simply editing the XML configuration file. Let’s change our application to use the Cat implementation for the Animal type. Modify the <bean> tag in applicationContext.xml as follows:

<bean id="myAnimal" class="com.javacodegeeks.examples.beans.Cat" />

Save the changes and rerun the application.  You will see the following output in the console:

I like to nap!

3.5 Java Configuration

We can also provide configuration
metadata to the application context using Java configuration. With Java configuration, we no longer require XML to configure
beans in the application.

A class that is annotated with @Configuration can serve as a source of bean definitions. You write methods inside the configuration class that instantiate and otherwise configure objects. These methods are then annotated with @Bean. This will register the beans in the application context, where they will be managed by the IoC container.  

Since @SpringBootApplication is meta-annotated with @SpringBootConfiguration, we can add our bean definitions directly in the IocSpringApplication class. (You may want to create your own configuration classes for this purpose.) Add the following method to IocSpringApplication:

IocSpringApplication.java

// other import statements
import com.javacodegeeks.examples.beans.Dog;

@SpringBootApplication
public class IocSpringApplication {
	
	@Bean
	public Animal dog() {
		return new Dog();
	}
// more code...
}

At this point, you may ask, what is the id of the defined bean? By default, the bean id is the name of the method. For example, the bean id for the Dog bean is “dog”. You can change the default bean id name by specifying the name attribute to the @Bean annotation, e.g. @Bean(name="Dog") or simply @Bean("myDog").

We will now update the class to use the configured bean. Change the main method as follows:

IocSpringApplication.java

	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(
			IocSpringApplication.class, args);
		
		Animal animal = context.getBean("dog", Animal.class);
		
		System.out.println(animal.getFavoritePastime());
		
		context.close();
	}

Finally, remove the @ImportResources annotation. We no longer require it. Save the file and rerun the application. You will see the following in the console:

I like to bark!

3.6 Annotation-Based Configuration

We can also provide configuration metadata using annotation-based configuration. During application startup, Spring will scan specific packages for Java classes annotated with @Component and other specialized annotations. This process is known as component scanning.

Here are some of the specialized (aka stereotype) annotations
Spring will search for during the component scan process:

  • @Controller
  • @Service
  • @Repository
  • @Autowired

Spring will automatically register these annotated classes as beans in the application context.

Notes:

  • These specialized annotations are meta-annotated with @Component.
  • Any annotation that inherits from a stereotype annotation will also be registered in the application context.

You can specify the packages to be scanned with the @ComponentScan annotation. In a Spring boot application, the package containing the Spring Boot application class (e.g. IocSpringApplication in our example) and its sub-packages (e.g. com.javacodegeeks.examples.beans) will automatically get scanned.

Add the @Component annotation to the Dog class as shown:

Dog.java

@Component
public class Dog implements Animal {

	@Override
	public String getFavoritePastime() {
		return "I like to bark!";
	}
}

By default, the bean id is the name of the class in the camel-case with the first character in the lower-case. For example, the bean id for the Dog bean is "dog". You can change the default bean id name by specifying the name attribute to the @Component annotation, e.g. @Component (name="myDog") or simply @Component ("myDog").

Modify the IocSpringApplication  class by removing the @Bean method since it is now redundant. Save the changes and rerun the application.

3.6.1 Autowiring Dependencies

Another way to use DI is with the @Autowired annotation. Basically, it allows the Spring container to automatically resolve dependencies between collaborating beans by inspecting the beans that have been defined.

First, let’s create a new class that will bring the necessary configuration to use the @Autowired annotation:

AppConfig.java

@Configuration
@ComponentScan("com.javacodegeeks.examples")
public class AppConfig {

}

This class allows the Spring container to scan for Beans in the "com.javacodegeeks.examples" package (as specified by @ComponentScan) that has the @Component annotation. As a result, the container loads these classes into the application context where they become eligible for injection.

Now, we will create another dependent class to inject this bean using @Autowired annotation. Conventionally, this type of class is called Service, but for this example we will call AnimalActions.java.

AnimalActions.java

@Component
public class AnimalActions {

	@Autowired
	public Animal dog;
	
	public String doStuff() {
		return dog.getFavoritePastime();
	}
}

The AnimalActions class is marked with the @Component annotation that is located by the Spring Container. The @Autowired annotation will inject a new Animal object. Let’s mark the Dog class with the @Component annotation, so that it will be loaded in the container as an Animal bean.

Dog.class

@Component
public class Dog implements Animal {

	@Override
	public String getFavoritePastime() {
		return "I like to bark!";
	}

}

Finally, we will modify the IocSpringApplication class:

IocSpringApplication.java with AnnotationConfigApplicationConext

@SpringBootApplication
public class IocSpringApplication {

	
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		
		AnimalActions animalAction = context.getBean(AnimalActions.class);
        
        System.out.println(animalAction.doStuff());
        
        context.close();
	}

}

So, now we are using AnnotationConfigApplicationContext that includes the AppConfig in order to load classes which has the @Component annotation. In conclusion, we can see some changes in the output when the program will run. Last but not least, we can observe how Spring Container loads the classes:

DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/slauriano/dev/ioc-spring-example/annotation-ioc-spring/target/classes/com/javacodegeeks/examples/AnimalActions.class]
DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/slauriano/dev/ioc-spring-example/annotation-ioc-spring/target/classes/com/javacodegeeks/examples/IocSpringApplication.class]
DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/slauriano/dev/ioc-spring-example/annotation-ioc-spring/target/classes/com/javacodegeeks/examples/beans/Dog.class]
DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/slauriano/dev/ioc-spring-example/annotation-ioc-spring/target/classes/com/javacodegeeks/examples/AnimalActions.class]
DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/slauriano/dev/ioc-spring-example/annotation-ioc-spring/target/classes/com/javacodegeeks/examples/AppConfig.class]

4. Summary

In this article, we discussed the inversion of control in Spring and how it is used to write code that is loosely coupled.

5. Download the Source Code

This was an example about the inversion of control in Spring.

Download
You can download the full source code of this example here:
IoC in Spring Example

Last updated on Oct. 21st, 2020

Gilbert Lopez

Gilbert Lopez is an application developer and systems integration developer with experience building business solutions for large and medium-sized companies. He has worked on many Java EE projects. His roles have included lead developer, systems analyst, business analyst and consultant. Gilbert graduated from California State University in Los Angeles with a Bachelor of Science degree in Business.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button