Suivi des modifications d'objets en Java avec réflexion et annotations

Objectif : Lors de la mise à jour d'un objet, il est nécessaire de consigner les détails des changements, notamment le nom du champ, sa valeur avant modification et sa valeur après modification.

Aprpoche : Comparer deux instances d'un même bean pour identifier les écarts de valeurs. Afin de réutiliser cette logique pour différents types de beans, on exploite les génériques et la réflexion pour une implémentation générique.

Étapes d'implémentation

Définition d'une annotation personnalisée

package com.example.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MarquerChangement {
    String libelle() default "";
}

Application de l'annotation aux champs du bean

package com.example.modele;

import com.example.annotation.MarquerChangement;
import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class Employe {
    private Long identifiant;
    @MarquerChangement(libelle = "Nom complet")
    private String nom;
    @MarquerChangement(libelle = "Âge")
    private Integer age;
}

Implémentation du détecteur de changements

package com.example.util;

import com.example.annotation.MarquerChangement;
import java.lang.reflect.Field;
import java.util.Objects;

public class DetecteurChangement {

    public static <T> String extraireModifications(T objetMisAJour, T objetInitial) {
        Field[] champs = objetMisAJour.getClass().getDeclaredFields();
        StringBuilder resultat = new StringBuilder();
        for (Field champ : champs) {
            champ.setAccessible(true);
            if (champ.isAnnotationPresent(MarquerChangement.class)) {
                try {
                    Object nouvelleValeur = champ.get(objetMisAJour);
                    Object ancienneValeur = champ.get(objetInitial);
                    if (!Objects.equals(nouvelleValeur, ancienneValeur)) {
                        String libelle = champ.getAnnotation(MarquerChangement.class).libelle();
                        resultat.append(libelle)
                                .append(" : Anciennement = ")
                                .append(ancienneValeur)
                                .append(", Nouvellement = ")
                                .append(nouvelleValeur)
                                .append("\n");
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return resultat.toString();
    }
}

Exemple de test

package com.example;

import com.example.modele.Employe;
import com.example.util.DetecteurChangement;

public class TestDetecteur {
    public static void main(String[] args) {
        Employe avant = Employe.builder()
                .identifiant(101L)
                .nom("Alice Martin")
                .age(30)
                .build();

        Employe apres = Employe.builder()
                .identifiant(101L)
                .nom("Alice Dupont")
                .age(31)
                .build();

        String changements = DetecteurChangement.extraireModifications(apres, avant);
        System.out.println(changements);
        // Sortie attendue :
        // Nom complet : Anciennement = Alice Martin, Nouvellement = Alice Dupont
        // Âge : Anciennement = 30, Nouvellement = 31
    }
}

Étiquettes: Java réflexion Annotations génériques Suivi des modifications

Publié le 15 juin à 00h07