Techniques de copie et de clonage d'objets Java avec Hutool et Apache Commons Lang

BeanUtil du module core de Hutool

La classe cn.hutool.core.bean.BeanUtil centralise un ensemble d'outils destinés à la manipulation de JavaBeans. Elle permet notamment de copier des propriétés, de convertir un bean en Map et inversement, ou encore de remplir un objet à partir d'une source de données structurée.

Voici les capacités les plus utilisées :

  • Conversion bean vers Map : beanToMap(Object bean, boolean isToUnderlineCase, boolean ignoreNullValue) produit une Map à partir d'un bean, avec la possibilité de convertir les noms de champs en snake_case et d'ignorer les valeurs null.
  • Conversion avec éditeur de clés : beanToMap(Object bean, Map<String, Object> targetMap, boolean ignoreNullValue, Editor<String> keyEditor) permet d'intervenir sur le nom des clés via l'interface Editor.
  • Copie de propriétés simple : copyProperties(Object source, Object target, String... ignoreProperties) recopie les propriétés d'une source vers une cible, en ignorant certains champs si nécessaire.
  • Copie avancée : copyProperties(Object source, Object target, CopyOptions copyOptions) offre davantage de contrôle grâce à l'objet CopyOptions.
  • Conversion d'une collection : copyToList(Collection<?> collection, Class<T> targetType) copie les propriétés d'une collection vers une nouvelle liste d'un autre type.
  • Conversion d'un bean vers un autre type : toBean(Object source, Class<T> targetType) crée un nouvel objet du type cible et y recopie les propriétés de la source.
  • Remplissage depuis une Map : fillBeanWithMap(Map<String, Object> map, Object bean) injecte les valeurs d'une Map dans un bean.
  • Introspection : Hutool propose un accès simplifié à l'API JavaBeans, par exemple pour lister les descripteurs de propriétés ou obtenir un dictionnaire champ/description.

Copie d'une collection avec BeanUtil.copyToList

Cette méthode parcourt chaque élément d'une collection source, crée une instance du type cible et recopie les propriétés. Elle dispose de deux signatures principales : sans options et avec CopyOptions.

// Copie simple
List<ClientDTO> dtos = BeanUtil.copyToList(clients, ClientDTO.class);

// Copie avec options personnalisées
CopyOptions regles = CopyOptions.create()
        .setIgnoreNullValue(true)
        .setIgnoreProperties("motDePasse");
List<ClientVO> vues = BeanUtil.copyToList(dtos, ClientVO.class, regles);

Elle exploite en interne le mécanisme BeanCopier de Hutool pour garantir la correspondance des champs et une bonne performance, sans recourir explicitement à la réflexion.

Copie rapide avec CglibUtil.copy

La méthode cn.hutool.extra.cglib.CglibUtil.copy s'appuie sur CGLIB pour copier les propriétés d'un objet vers un autre. Elle permet de cibler une classe, une instance existante, ou d'appliquer un Converter pour transformer les valeurs pendant la copie.

Produit p = new Produit();
p.setNom("Ordinateur portable");

// Création d'une nouvelle instance cible
ProduitVue vue = CglibUtil.copy(p, ProduitVue.class);

// Copie vers une instance déjà créée
ProduitVue vueExistante = new ProduitVue();
CglibUtil.copy(p, vueExistante);

// Copie avec conversion personnalisée
CglibUtil.copy(p, vueExistante, new Converter() {
    @Override
    public Object convert(Object valeur, Class<?> classeCible, Object contexte) {
        // Logique de transformation
        return valeur;
    }
});

Contrairement à BeanUtil.copyProperties, cette approche ne propose pas d'ignorer directement des propriétés. Elle met en cache les BeanCopier générés par CGLIB selon les classes concernées, ce qui offre d'excellentes performances lors des copies répétées.

Pour utiliser CglibUtil, assurez-vous que la dépendance CGLIB est bien présente :

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

Copie d'une liste avec CglibUtil.copyList

La méthode copyList est dédiée aux collections. Elle accepte un Supplier<T> pour créer chaque nouvelle instance de la liste cible.

List<Commande> commandesSource = // ... initialisation

List<CommandeCopie> commandesCibles = CglibUtil.copyList(
        commandesSource,
        CommandeCopie::new
);

Des surcharges acceptent un Converter ou un BiConsumer pour intervenir avant ou après la copie de chaque élément. Comme pour CglibUtil.copy, cette méthode effectue une copie superficielle et ne permet pas de spécifier des propriétés à ignorer directement.

Clonage profond avec SerializationUtils.clone

La méthode org.apache.commons.lang3.SerializationUtils.clone d'Apache Commons Lang réalise un clonage profond en s'appuyant sur le mécanisme de sérialisation Java. Tous les objets concernés doivent implémenter java.io.Serializable.

import org.apache.commons.lang3.SerializationUtils;

public class ExempleClonage {
    public static void main(String[] args) {
        Configuration originale = new Configuration();
        originale.setHote("exemple.com");

        Configuration copie = SerializationUtils.clone(originale);

        // 'copie' est une copie complètement indépendante
    }
}

Cette approche est thread-safe et simple d'utilisation, mais la sérialisation peut être coûteuse pour des graphes d'objets complexes ou de grande taille.

Comparaison des approches

Méthode Librairie Usage principal Type de copie Remarques
BeanUtil Hutool Core Copie et conversion de beans, bean ⇄ Map Superficielle Idéal pour les copies simples et les conversions rapides.
BeanUtil.copyToList Hutool Core Copie d'une collection vers un autre type Superficielle Supporte CopyOptions pour ignorer des champs ou des valeurs null.
CglibUtil.copy Hutool Extra CGLIB Copie d'objets via CGLIB Superficielle Performances élevées grâce au cache des BeanCopier.
CglibUtil.copyList Hutool Extra CGLIB Copie d'une collection via Supplier Superficielle Permt d'utiliser un constructeur personnalisé pour chaque élément.
SerializationUtils.clone Apache Commons Lang3 Clonage profond d'un objet sérialisable Profonde Nécessite l'implémenattion de Serializable.

Étiquettes: Hutool BeanUtil CglibUtil CGLib Apache Commons Lang

Publié le 7 juillet à 18h49