Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Inversion of Control and XML Dependency Injection in Spring

Tech Apr 19 11

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 an abstract User class.

public abstract class User {
    protected String username;
}
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.

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:

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:

<bean id="client1" class="com.example.Customer">
    <constructor-arg name="username" value="alice_dev" />
    <constructor-arg name="loyaltyPoints" value="1200" />
</bean>

By Parameter Index:

<bean id="client2" class="com.example.Customer">
    <constructor-arg index="0" value="bob_admin" />
    <constructor-arg index="1" value="850" />
</bean>

Seter 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.

<bean id="client3" class="com.example.Customer">
    <constructor-arg index="0" value="charlie_ops" />
    <constructor-arg index="1" value="0" />
    <property name="loyaltyPoints" value="500" />
</bean>

Injecting Object References

When a dependancy is another bean, the ref attribute is used instead of value.

<bean id="hq_location" class="com.example.Location">
    <property name="state" value="California" />
    <property name="municipality" value="San Francisco" />
</bean>

<bean id="client4" class="com.example.Customer">
    <constructor-arg name="username" value="diana_mgr" />
    <constructor-arg name="loyaltyPoints" value="3400" />
    <constructor-arg name="residentialArea" ref="hq_location" />
</bean>

<bean id="client5" class="com.example.Customer">
    <constructor-arg index="0" value="eve_dev" />
    <constructor-arg index="1" value="2100" />
    <constructor-arg index="2" ref="hq_location" />
</bean>

Retrieving and verifying the reference injection:

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 bean are scoped as singletons. Requesting the same bean ID yields the exact same instance.

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.

<bean id="client5" class="com.example.Customer" scope="prototype">
    <constructor-arg index="0" value="eve_dev" />
    <constructor-arg index="1" value="2100" />
    <constructor-arg index="2" ref="hq_location" />
</bean>

With the prototype scope, the equality check evaluates to false:

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 configuration errors immediately. If a singleton bean should only be instantiated upon its first invocation, the lazy-init attribute can be set to true.

<bean id="lazy_client" class="com.example.Customer" lazy-init="true" scope="singleton">
    <constructor-arg name="username" value="frank_test" />
    <constructor-arg name="loyaltyPoints" value="100" />
</bean>

Verifying the deferred instantiation:

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.");
    }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.