Complete Spring IoC Resource Guide
- IoC (Inversion of Control)/DI (Dependency Injection)
- AOP (Aspect-Oriented Programming)
Spring Overview
Spring is an enterprise-level development framework focused on software design, offering flexible component selection and application layer separation.
MVC: Struts2, Spring MVC
ORMapping: Hibernate, MyBatis, Spring Data
Why Use Spring (Enterprise Project Characteristics)
- Large scale: high user count, massive data, numerous functional modules
- High performance and security requiremants
- Complex business logic
- Frequent changes
Spring Advantages
- Low intrusive design
- Independence from application servers
- Dependency injection simplifies component relationships and reduces coupling
- Aspect-oriented programming enables centralized handling of common tasks
- Excellent integration with third-party frameworks
Spring IoC
What is Inversion of Control
In traditional development, the caller actively creates instances of called objects. In Spring, the IoC container manages object creation and pushes instances to callers, reversing the control flow.
How to Use IoC
Maven Project Setup
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>SpringDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Create Entity Class
package com.example.entity;
import lombok.Data;
@Data
public class Person {
private String fullName;
private long personId;
private int age;
private Location residence;
}
package com.example.entity;
import lombok.Data;
@Data
public class Location {
private long locationId;
private String address;
}
Traditional vs IoC Approach
Traditional (Manual Creation):
Person person = new Person();
person.setPersonId(101);
person.setFullName("Alice Smith");
person.setAge(30);
System.out.println(person);
IoC Configuration (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="myPerson" class="com.example.entity.Person">
<property name="personId" value="101"></property>
<property name="fullName" value="Alice Smith"></property>
<property name="age" value="30"></property>
<property name="residence" ref="myLocation"></property>
</bean>
<bean id="myLocation" class="com.example.entity.Location">
<property name="locationId" value="5001"></property>
<property name="address" value="123 Main St, NY"></property>
</bean>
</beans>
Retrieve Object from IoC:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.example.entity.Person;
public class IoCTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Person person = (Person) context.getBean("myPerson");
System.out.println(person);
}
}
Configuration File Details
- bean tag: manages objects
id: object identifierclass: fully qualified class name (requires no-arg constructor for reflection)
- property tag: injects properties
name: property namevalue: for primitive types and Stringref: references another bean (DI)
IoC Underlying Principles
Implement a simplified IoC container using DOM4J and reflection:
Add DOM4J Dependency
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
Implement ApplicationContext
package com.example.ioc;
public interface ApplicationContext {
Object getBean(String beanId);
}
Implement ClassPathXmlApplicationContext
package com.example.ioc;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class ClassPathXmlApplicationContext implements ApplicationContext {
private Map<String, Object> iocContainer = new HashMap<>();
public ClassPathXmlApplicationContext(String configPath) throws Exception {
SAXReader reader = new SAXReader();
Document document = reader.read("./src/main/resources/" + configPath);
Element root = document.getRootElement();
Iterator<Element> beanIterator = root.elementIterator();
while (beanIterator.hasNext()) {
Element bean = beanIterator.next();
String beanId = bean.attributeValue("id");
String className = bean.attributeValue("class");
Class<?> clazz = Class.forName(className);
Constructor<?> constructor = clazz.getConstructor();
Object instance = constructor.newInstance();
Iterator<Element> propertyIterator = bean.elementIterator();
while (propertyIterator.hasNext()) {
Element property = propertyIterator.next();
String propName = property.attributeValue("name");
String propValue = property.attributeValue("value");
String propRef = property.attributeValue("ref");
if (propRef == null) {
String setterName = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
Field field = clazz.getDeclaredField(propName);
Method setter = clazz.getDeclaredMethod(setterName, field.getType());
Object convertedValue = null;
String fieldType = field.getType().getName();
if (fieldType.equals("long")) {
convertedValue = Long.parseLong(propValue);
} else if (fieldType.equals("java.lang.String")) {
convertedValue = propValue;
} else if (fieldType.equals("int")) {
convertedValue = Integer.parseInt(propValue);
}
setter.invoke(instance, convertedValue);
}
}
iocContainer.put(beanId, instance);
}
}
@Override
public Object getBean(String beanId) {
return iocContainer.get(beanId);
}
}
Test Custom IoC
import com.example.entity.Person;
import com.example.ioc.ApplicationContext;
import com.example.ioc.ClassPathXmlApplicationContext;
public class CustomIoCTest {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Person person = (Person) context.getBean("myPerson");
System.out.println(person);
}
}
Bean Scopes
- singleton (default): single instence per IoC container
- prototype: new instance for each request
- request: valid within an HTTP request
- session: valid within a user session
Spring Inheritance
Object-level inheritance: child bean inherits parent bean's properties (class can differ if properties match)
<bean id="basePerson" class="com.example.entity.Person">
<property name="personId" value="200"></property>
<property name="fullName" value="Base User"></property>
</bean>
<bean id="extendedPerson" class="com.example.entity.Person" parent="basePerson">
<property name="age" value="28"></property>
</bean>
Spring Dependency
Specifies creation order: depends-on ensures dependent bean is created first
<bean id="employee" class="com.example.entity.Employee" depends-on="department"></bean>
<bean id="department" class="com.example.entity.Department"></bean>
P Namespace
Simplifies property injection
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="personBean" class="com.example.entity.Person"
p:personId="300" p:fullName="Bob Brown" p:age="35"
p:residence-ref="locationBean"></bean>
<bean id="locationBean" class="com.example.entity.Location"
p:locationId="6001" p:address="456 Oak Ave, LA"></bean>
</beans>
Factory Methods
Static Factory
package com.example.factory;
import com.example.entity.Product;
import java.util.HashMap;
import java.util.Map;
public class StaticProductFactory {
private static Map<Long, Product> productMap;
static {
productMap = new HashMap<>();
productMap.put(100L, new Product(100L, "Laptop"));
productMap.put(101L, new Product(101L, "Smartphone"));
}
public static Product getProduct(long productId) {
return productMap.get(productId);
}
}
Config:
<bean id="myProduct" class="com.example.factory.StaticProductFactory"
factory-method="getProduct">
<constructor-arg value="100"></constructor-arg>
</bean>
Instance Factory
package com.example.factory;
import com.example.entity.Product;
import java.util.HashMap;
import java.util.Map;
public class InstanceProductFactory {
private Map<Long, Product> productMap;
public InstanceProductFactory() {
productMap = new HashMap<>();
productMap.put(200L, new Product(200L, "Tablet"));
productMap.put(201L, new Product(201L, "Watch"));
}
public Product getProduct(long productId) {
return productMap.get(productId);
}
}
Config:
<bean id="productFactory" class="com.example.factory.InstanceProductFactory"></bean>
<bean id="myInstanceProduct" factory-bean="productFactory"
factory-method="getProduct">
<constructor-arg value="200"></constructor-arg>
</bean>
Autowiring
Automatically injects dependencies without explicit property tags
byName
Injects by property name matching bean id
<bean id="myPerson" class="com.example.entity.Person" autowire="byName">
<property name="personId" value="400"></property>
<property name="fullName" value="Charlie Davis"></property>
</bean>
<bean id="residence" class="com.example.entity.Location">
<property name="locationId" value="7001"></property>
<property name="address" value="789 Pine Rd, Chicago"></property>
</bean>
byType
Injects by property type (requires unique bean type)
<bean id="myPerson" class="com.example.entity.Person" autowire="byType">
<property name="personId" value="400"></property>
<property name="fullName" value="Charlie Davis"></property>
</bean>
<bean class="com.example.entity.Location">
<property name="locationId" value="7001"></property>
<property name="address" value="789 Pine Rd, Chicago"></property>
</bean>