Initialisation du conteneur IoC Spring et injection de dépendances via XML

Le cœur du framework Spring réside dans son conteneur d'inversion de contrôle (IoC). La classe ClassPathXmlApplicationContext permet de charger les définitions de composants à partir d'un fichier de configurasion XML situé dans le classpath. Une fois le conteneur initialisé, il gère le cycle de vie des objets et permet d'extraire les instances prêtes à l'emploi.

Instanciation du conteneur et récupération des beans

Le processus se déroule en deux étapes principales : la création du contexte applicatif et l'extraction des composants. Un même conteneur peut gérer et fournir de multiples instances de différents types. Il est possible de récupérer un bean par son identifiant unique ou directement par son type (à condition qu'une seule instance de ce type existe dans le contexte).

package com.example.spring.demo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringContainerDemo {
    public static void main(String[] args) {
        // Initialisation du contexte Spring à partir du fichier XML
        // L'utilisation de try-with-resources assure la fermeture correcte du conteneur
        try (ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("application-context.xml")) {

            // Récupération d'un bean par son identifiant
            MessageService messageService = (MessageService) appContext.getBean("messageService");
            messageService.setRecipient("Alice");
            messageService.send();

            // Récupération d'un bean par son type 
            // Nécessite qu'il n'y ait qu'une seule instance de ce type dans le conteneur
            Vehicle vehicle = appContext.getBean(Vehicle.class);
            System.out.println("Véhicule récupéré par type : " + vehicle);

            // Récupération d'un bean avec des propriétés de collection
            Customer customer = (Customer) appContext.getBean("premiumCustomer");
            System.out.println("Client avec sa flotte : " + customer);
        }
    }
}

Configuration XML avancée

Le fichier de configuration XML prend en charge diverses stratégies d'injection de dépendances. Cela inclut l'injection par mutateru (setter), l'injection par constructeur (avec gestion des index et des types), les beans internes, les propriétés en cascade, ainsi que la gestion des collections, l'héritage de définitions et les dépendances explicites.

<?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"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- Injection par mutateur (setter) -->
    <bean id="messageService" class="com.example.spring.demo.MessageService">
        <property name="recipient" value="UtilisateurParDefaut"/>
    </bean>

    <!-- Injection par constructeur avec index et type -->
    <bean id="sedanVehicle" class="com.example.spring.demo.Vehicle">
        <constructor-arg value="Toyota" index="0"/>
        <constructor-arg value="Camry" index="1"/>
        <constructor-arg value="28000.0" type="double"/>
    </bean>

    <!-- Injection par constructeur avec caractères spéciaux (CDATA) -->
    <bean id="suvVehicle" class="com.example.spring.demo.Vehicle">
        <constructor-arg value="Ford"/>
        <constructor-arg>
            <value><![CDATA[<Explorer Limited>]]></value>
        </constructor-arg>
        <constructor-arg value="45000.0" type="double"/>
    </bean>

    <!-- Bean interne et référence -->
    <bean id="dataRepository" class="com.example.spring.demo.DataRepository">
        <property name="connectionString" value="jdbc:mysql://localhost:3306/db"/>
    </bean>

    <bean id="businessService" class="com.example.spring.demo.BusinessService">
        <property name="repository">
            <!-- Bean interne : similaire à une classe anonyme, non référencable en dehors -->
            <bean class="com.example.spring.demo.DataRepository">
                <property name="connectionString" value="jdbc:h2:mem:testdb"/>
            </bean>
        </property>
    </bean>

    <!-- Propriétés en cascade -->
    <bean id="apiController" class="com.example.spring.demo.ApiController">
        <property name="service" ref="businessService"/>
        <property name="service.repository.connectionString" value="jdbc:postgresql://localhost:5432/prod"/>
    </bean>

    <!-- Collections et util:list -->
    <util:list id="vehicleFleet">
        <ref bean="sedanVehicle"/>
        <ref bean="suvVehicle"/>
    </util:list>

    <!-- Héritage de bean et dépendances -->
    <bean id="baseCustomer" class="com.example.spring.demo.Customer" abstract="true">
        <property name="vehicles" ref="vehicleFleet"/>
    </bean>

    <bean id="vipCustomer" parent="baseCustomer" p:name="John Doe"/>

    <bean id="premiumCustomer" parent="baseCustomer" p:name="Jane Smith" depends-on="vipCustomer"/>

</beans>

Définition des classes métier (POJO)

Les beans configurés dans le fichier XML doivent correspondre à des classes Java stendard. Elles doivent être équipées des constructeurs et mutateurs (setters) nécessaires pour permettre au conteneur d'injecter les dépendances et les valeurs de propriétés.

package com.example.spring.demo;

public class MessageService {
    private String recipient;

    public MessageService() {
        System.out.println("Initialisation de MessageService via constructeur sans argument...");
    }

    public void setRecipient(String recipient) {
        this.recipient = recipient;
    }

    public void send() {
        System.out.println("Envoi du message à : " + recipient);
    }
}

package com.example.spring.demo;

public class Vehicle {
    private String brand;
    private String model;
    private double price;

    public Vehicle(String brand, String model, double price) {
        this.brand = brand;
        this.model = model;
        this.price = price;
    }

    @Override
    public String toString() {
        return brand + " " + model + " (" + price + " EUR)";
    }
}

package com.example.spring.demo;

import java.util.List;

public class Customer {
    private String name;
    private List<Vehicle> vehicles;

    public void setName(String name) {
        this.name = name;
    }

    public void setVehicles(List<Vehicle> vehicles) {
        this.vehicles = vehicles;
    }

    @Override
    public String toString() {
        return "Customer{name='" + name + "', vehicles=" + vehicles + '}';
    }
}

package com.example.spring.demo;

public class DataRepository {
    private String connectionString;

    public void setConnectionString(String connectionString) {
        this.connectionString = connectionString;
    }

    public String getConnectionString() {
        return connectionString;
    }
}

package com.example.spring.demo;

public class BusinessService {
    private DataRepository repository;

    public void setRepository(DataRepository repository) {
        this.repository = repository;
    }

    public DataRepository getRepository() {
        return repository;
    }
}

package com.example.spring.demo;

public class ApiController {
    private BusinessService service;

    public void setService(BusinessService service) {
        this.service = service;
    }

    public BusinessService getService() {
        return service;
    }
}

Étiquettes: spring-framework ioc-container dependency-injection xml-configuration java-beans

Publié le 18 juillet à 01h03