Inversion of Control and XML Dependency Injection in Spring
Inversion of Control (IoC) shifts the responsibility of object creation and lifecycle management away from the objects themselves, delegating it to an external container. Dependency Injection (DI) serves as the primary mechanism to achieve this, where required components are supplied to an object rather than the object constructing them directly. This approach eliminates tight coupling caused by hard-coded instantiation.
Within the Spring ecosystem, the IoC container manages application components known as Beans. These beans can be defined using XML, annotations, or code-based configurations.
XML-Based Dependency Injection
Consider a class hierarchy where Customer extends a abstract User class.
java public abstract class User { protected String username; }
java public class Customer extends User { private int loyaltyPoints; private Location residentialArea;
public Customer(String username, int loyaltyPoints) {
this.username = username;
this.loyaltyPoints = loyaltyPoints;
}
public Customer(String username, int loyaltyPoints, Location residentialArea) {
this.username = username;
this.loyaltyPoints = loyaltyPoints;
this.residentialArea = residentialArea;
}
public void setLoyaltyPoints(int loyaltyPoints) {
this.loyaltyPoints = loyaltyPoints;
}
@Override
public String toString() {
return "Customer{username='" + username + "', loyaltyPoints=" + loyaltyPoints + ", location=" + residentialArea + "}";
}
}
The Location class represents a reference type dependency.
java public class Location { private String state; private String municipality;
public Location() {}
public Location(String state, String municipality) {
this.state = state;
this.municipality = municipality;
}
public String getState() { return state; }
public void setState(String state) { this.state = state; }
public String getMunicipality() { return municipality; }
public void setMunicipality(String municipality) { this.municipality = municipality; }
@Override
public String toString() {
return "Location{state='" + state + "', municipality='" + municipality + "'}";
}
}
To bootstrap the container and retrieve beans:
java import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Application { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("service-config.xml"); User client1 = context.getBean("client1", User.class); System.out.println(client1); } }
Constructor Argument Injection
Dependencies can be injected during instantiation using the constructor-arg element.
By Parameter Name:
xml
By Parameter Index:
xml
Setter Method Injection
Properties can be assigned after construction using the property element. This requires the target class to provide corresponding setter methods for the fields.
xml
Injecting Object References
When a dependency is another bean, the ref attribute is used instead of value.
xml
Retrieving and verifying the reference injection:
java ApplicationContext context = new ClassPathXmlApplicationContext("service-config.xml"); User client4 = context.getBean("client4", User.class); User client5 = context.getBean("client5", User.class); System.out.println(client4); System.out.println(client5);
Configuring Bean Scope
By default, Spring beans are scoped as singletons. Requesting the same bean ID yields the exact same instance.
java User clientA = context.getBean("client5", User.class); User clientB = context.getBean("client5", User.class); System.out.println(clientA == clientB); // Outputs: true
To instruct the container to produce a new instance for every request, set the scope attribute to prototype.
xml
With the prototype scope, the equality check evaluates to false:
java User clientA = context.getBean("client5", User.class); User clientB = context.getBean("client5", User.class); System.out.println(clientA == clientB); // Outputs: false
Lazy Initialization
The ApplicationContext pre-instantiates all singleton beans during startup. This fail-fast behavior exposes configuraton errors immediately. If a singleton bean should only be instantiated upon its first invocation, the lazy-init attribute can be set to true.
xml
Verifying the deferred instantiation:
java public class Application { public static void main(String[] args) throws InterruptedException { ApplicationContext context = new ClassPathXmlApplicationContext("service-config.xml"); System.out.println("Container initialized."); Thread.sleep(2000);
User lazyClient = context.getBean("lazy_client", User.class);
System.out.println("Lazy bean retrieved.");
}
}