Implementing Dependency Injection with Spring's IOC Container
Spring's Inversion of Control (IOC) is realized through Dependency Enjection (DI), which offers several advantages:
- Minimizes object creation and management, enhancing code clarity.
- The lightweight, non-invasive Spring IOC container avoids dependency on container-specific APIs and doesn't require special interface implementations.
- Encourages programming to interfaces.
- Reduces code coupling by externalizing dependencies to configuration files, simplifying relationship changes.
- Provides declarative AOP services.
XML-Based Dependency Injection
XML configuration supports constructor injection, setter injection, and rarely used interface injection. Setter injection is demonstrated below.
Injecting Common POJO Properties
A Plain Old Java Object (POJO) without interfaces or parent classes can have properties injected via XML.
POJO Class:
package com.example.spring.entity;
import java.util.*;
public class Customer {
private String name;
private int id;
private List<String> preferences;
private Set<String> categories;
private String[] aliases;
private Map<String, String> metadata;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public List<String> getPreferences() { return preferences; }
public void setPreferences(List<String> preferences) { this.preferences = preferences; }
public Set<String> getCategories() { return categories; }
public void setCategories(Set<String> categories) { this.categories = categories; }
public String[] getAliases() { return aliases; }
public void setAliases(String[] aliases) { this.aliases = aliases; }
public Map<String, String> getMetadata() { return metadata; }
public void setMetadata(Map<String, String> metadata) { this.metadata = metadata; }
}
Configruation File (customer-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="customer" class="com.example.spring.entity.Customer">
<property name="name" value="John Doe"/>
<property name="id" value="1001"/>
<property name="preferences">
<list>
<value>Reading</value>
<value>Traveling</value>
</list>
</property>
<property name="categories">
<set>
<value>Premium</value>
<value>Active</value>
</set>
</property>
<property name="aliases">
<list>
<value>JD</value>
<value>John</value>
</list>
</property>
<property name="metadata">
<map>
<entry key="region" value="North"/>
<entry key="status" value="Verified"/>
</map>
</property>
</bean>
</beans>
Test Class:
package com.example.spring.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.example.spring.entity.Customer;
public class PropertyInjectionTest {
@Test
public void testPropertyInjection() {
ApplicationContext context = new ClassPathXmlApplicationContext("customer-config.xml");
Customer customer = (Customer) context.getBean("customer");
System.out.println("Name: " + customer.getName());
System.out.println("ID: " + customer.getId());
System.out.println("Preferences: " + customer.getPreferences());
System.out.println("Categories: " + customer.getCategories());
System.out.println("Aliases: " + Arrays.toString(customer.getAliases()));
System.out.println("Metadata: " + customer.getMetadata());
}
}
Bean Scope Configuration
The scope attribute defines bean instantiation behavior:
singleton: Single instance per container (default).prototype: New instance on each request.
Configuration (scope-config.xml):
<bean id="product" class="com.example.spring.entity.Product" scope="prototype"/>
Test:
ApplicationContext context = new ClassPathXmlApplicationContext("scope-config.xml");
Product prod1 = (Product) context.getBean("product");
Product prod2 = (Product) context.getBean("product");
System.out.println(prod1 == prod2); // false for prototype
Lazy Initialization
Set lazy-init="true" to defer bean creation until first use, or default-lazy-init="true" for all beans in a configurasion.
Configuration (lazy-config.xml):
<bean id="order" class="com.example.spring.entity.Order" lazy-init="true"/>
Autowiring Dependencies
Spring can automatically inject dependencies using autowire:
byName: Matches bean ID with property name.byType: Matches bean type (requiresautowire-candidate="false"to exclude candidates).
Configuration (autowire-config.xml):
<bean id="accountDao" class="com.example.spring.dao.AccountDaoImpl"/>
<bean id="alternateDao" class="com.example.spring.dao.AlternateDaoImpl" autowire-candidate="false"/>
<bean id="accountService" class="com.example.spring.service.AccountService" autowire="byType"/>
Loading Configuration Files
Single File
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
// or
ApplicationContext context = new FileSystemXmlApplicationContext("/absolute/path/config.xml");
Multiple Files
// Array method
String[] files = {"config1.xml", "config2.xml"};
ApplicationContext context = new ClassPathXmlApplicationContext(files);
// Pattern matching
ApplicationContext context = new ClassPathXmlApplicationContext("config-*.xml");
// Master configuration file
ApplicationContext context = new ClassPathXmlApplicationContext("master-config.xml");
Master Configuration (master-config.xml):
<beans>
<import resource="config1.xml"/>
<import resource="config2.xml"/>
</beans>
Annotation-Based Dependency Injection
Include spring-aop.jar and configure component scanning.
Configuration (annotation-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"
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">
<context:component-scan base-package="com.example.spring"/>
</beans>
Stereotype Annotations:
@Component: Generic component.@Service: Service layer class.@Repository: Data access class.@Controller: Web controller class.@Scope("prototype"): Defines bean scope.
Dependency Injection Annotations:
@Autowired: Injects by type, falls back to name.@Qualifier("beanName"): Specifies bean name with@Autowired.@Resource: Injects by name, falls back to type.
Service Class:
package com.example.spring.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.spring.dao.PaymentDao;
@Service("paymentService")
public class PaymentService {
@Autowired
private PaymentDao paymentDao;
public void setPaymentDao(PaymentDao paymentDao) {
this.paymentDao = paymentDao;
}
public boolean processPayment(String account, double amount) {
return paymentDao.process(account, amount);
}
}
DAO Implementation:
package com.example.spring.dao.impl;
import org.springframework.stereotype.Repository;
import com.example.spring.dao.PaymentDao;
@Repository("paymentDao")
public class PaymentDaoImpl implements PaymentDao {
public boolean process(String account, double amount) {
System.out.println("Processing payment via DAO");
return true;
}
}
Test:
ApplicationContext context = new ClassPathXmlApplicationContext("annotation-config.xml");
PaymentService service = (PaymentService) context.getBean("paymentService");
service.processPayment("ACC001", 250.75);