Spring 6 IoC Container and Bean Configuration Basics
The Spring Framework is a layered Java platform that provides comprehensive infrastructure support for developing enterprise applications. At the heart of the framwork is the Inversion of Control (IoC) container, which instantiates, configures, and assembles application objects, known as beens.
Spring in the broader sense refers to the entire ecosystem of projects (Spring Boot, Spring Cloud, Spring Data, etc.) built around the core Spring Framework. The framework itself focuses on two fundamental concepts: IoC and Aspect-Oriented Programming (AOP).
Key characteristics of the Spring Framework:
- Non-invasive: Spring minimizes its footprint on application code. Domain objects remain completely unaware of the framework, and components are marked with lightweight annotations.
- Inversion of Control: Instead of objects obtaining their dependencies from the environment, the container prepares and injects them.
- Aspect-Oriented Programming: Enables cross-cutting concerns (logging, transactions, security) to be encapsulated and applied without modifying business logic.
- Container: The IoC container manages the full lifecycle of beans, abstracting away complex initialization and wiring details.
- Component-based: Applications are assembled from reusable, clearly bounded components configured via XML, annotations, or Java configuration.
- Comprehensive: Spring integrates with a wide range of enterprise technologies, and many common requirements can be satisfied using Spring projects alone.
Setting Up a Spring 6 Project
Add the following dependencies to pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.3</version>
<scope>test</scope>
</dependency>
</dependencies>
Defining a Managed Bean
Create a simple service class:
package com.example.service;
public class MessageService {
public void displayMessage() {
System.out.println("Hello from Spring-managed bean!");
}
}
Bean configuration in spring-config.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageService" class="com.example.service.MessageService" />
</beans>
Bootstrapping the Container
A test class loads the configuration and retrieves the bean:
package com.example.service;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MessageServiceTest {
@Test
void shouldInvokeService() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
MessageService service = ctx.getBean("messageService", MessageService.class);
System.out.println("Bean instance: " + service);
service.displayMessage();
}
}
Reflection: Spring’s Internal Mechanism
Spring uses reflection to instantiate beans from class names at runtime. The following snippet mirrors this process:
@Test
void instantiateViaReflection() throws Exception {
Class<?> clazz = Class.forName("com.example.service.MessageService");
MessageService instance = (MessageService) clazz.getDeclaredConstructor().newInstance();
System.out.println("Reflection-created instance: " + instance);
instance.displayMessage();
}
The default, no-argument constructor is invoked just as the Spring container does when no custom initialization logic is77 defined.