The Spring IoC container is responsible for managing the complete lifecycle of application objects, known as beans. The container uses DI to inject dependencies into beans, allowing for more modular, testable, and maintainable code. By managing beans’ lifecycle and dependencies, the Spring container promotes loose coupling and adherence to the Single Responsibility Principle.
There are two main types of Spring containers:
The architecture of the Spring container can be divided into several key components:
InitializingBean
, DisposableBean
, and custom init/destroy methods.In constructor injection, the required dependencies are provided through a class constructor.
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
}
In setter injection, dependencies are provided through setter methods.
console.log( 'Code is Poetry' );
The Spring IoC container can be configured using:
@Configuration
to define beans.Let’s create a simple Spring application that demonstrates the IoC container, dependency injection, and various configuration methods. We’ll build a small application to simulate a car rental service.
You can set up a Spring project using Maven or Gradle. Here’s a sample pom.xml
for Maven:
4.0.0
com.example
spring-car-rental
1.0-SNAPSHOT
org.springframework
spring-context
5.3.10
org.springframework
spring-core
5.3.10
Create the following classes: Engine
, Car
, and RentalService
.
// Engine.java
public class Engine {
private String type;
public Engine(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
// Car.java
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
System.out.println("Car with " + engine.getType() + " engine started.");
}
}
// RentalService.java
public class RentalService {
private Car car;
public RentalService(Car car) {
this.car = car;
}
public void rentCar() {
System.out.println("Renting out the car.");
car.start();
}
}
Create an XML configuration file beans.xml
:
Create a main application class to load the Spring context and execute the service:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CarRentalApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
RentalService rentalService = context.getBean(RentalService.class);
rentalService.rentCar();
}
}
When you run the CarRentalApp
, you should see the output:
Renting out the car.
Car with V8 engine started.
Instead of using XML, we can configure our beans using annotations. Let’s modify our classes to use annotations:
import org.springframework.stereotype.Component;
@Component
public class Engine {
private String type = "V8";
public String getType() {
return type;
}
}
@Component
public class Car {
private final Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
System.out.println("Car with " + engine.getType() + " engine started.");
}
}
@Component
public class RentalService {
private final Car car;
public RentalService(Car car) {
this.car = car;
}
public void rentCar() {
System.out.println("Renting out the car.");
car.start();
}
}
Now, we need a configuration class to enable component scanning:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
And modify the CarRentalApp
class:
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CarRentalApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
RentalService rentalService = context.getBean(RentalService.class);
rentalService.rentCar();
}
}
When you run the application again, you’ll get the same output using annotation-based configuration.