Façon de répliquer les getters / setters pour les propriétés publiques dans un POJO


9

Nous avons un POJO qui est généré automatiquement avec environ 60 propriétés. Ceci est généré avec avro 1.4, qui n'inclut pas les getters / setters.

Une bibliothèque que nous utilisons pour fournir des transformations simples entre des objets nécessite des méthodes de type getter / setter pour fonctionner correctement.

Existe-t-il un moyen de répliquer les getters / setters sans avoir à remplacer manuellement le POJO et à créer tous les getters / setters manuellement?

public class BigGeneratedPojo {
  public String firstField;
  public int secondField;
  ...
  public ComplexObject nthField;
}

public class OtherObject {
  private String reprOfFirstFieldFromOtherObject;
  private ComplexObject reprOfFirstFieldFromOtherObject;
  public String getReprOfFirstFieldFromOtherObject() { ... standard impl ... };
  public void setReprOfFirstFieldFromOtherObject() { ... standard impl ... };
}

le souhait est d'écrire du code qui ressemble à:

Mapper<BigGeneratedPojo, OtherObject> mapper = 
  MagicalMapperLibrary.mapperBuilder(BigGeneratedPojo.class, OtherObject.class)
    .from(BigGeneratedPojo::getFirstField).to(OtherObject::reprOfFirstFieldFromOtherObject)
    .build();

BigGeneratedPojo pojo = new BigGeneratedPojo();
pojo.firstField = "test";

OtherObject mappedOtherObj = mapper.map(pojo);

assertEquals(mappedOtherObj.getReprOfFirstFieldFromOtherObject(), "test");

Réponses:


7

Vous pouvez essayer de générer dynamiquement les beans proxy, par exemple, en utilisant BitBuddy: https://bytebuddy.net/

L'exemple ci-dessous montre comment proxy un champ de propriété d'une méthode. Notez que ce n'est qu'un exemple, et très probablement vous devrez peut-être envelopper et ajouter de la dynamique en utilisant des réflexions, mais je pense que c'est une option assez intéressante si vous souhaitez étendre dynamiquement le code.

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.jar.asm.Opcodes;
import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class M1 {

    public static class PojoBase {
        int property;
        String strProp;
    }



    public static class Intereptor {

        private final String fieldName;
        private final PojoBase pojo;
        public Intereptor(PojoBase pojo, String fieldName) {
            this.pojo = pojo;
            this.fieldName = fieldName;
        }
        @RuntimeType
        public Object intercept(@RuntimeType Object value) throws NoSuchFieldException, IllegalAccessException {

            Field field = pojo.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(pojo, value);
            return value;
        }
    }



    public static void main(String... args) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
            PojoBase origBean = new PojoBase();
            PojoBase destBean = new PojoBase();

            origBean.property = 555666;
            origBean.strProp = "FooBar";

        DynamicType.Builder<Object> stub = new ByteBuddy()
            .subclass(Object.class);

        DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition<Object> dynamic = stub.defineMethod("getProperty", Integer.TYPE, Opcodes.ACC_PUBLIC).intercept(FixedValue.value(origBean.property))
                .defineMethod("setProperty", Void.TYPE, Opcodes.ACC_PUBLIC).withParameters(Integer.TYPE).intercept(MethodDelegation.to(new Intereptor(destBean, "property")))
                .defineMethod("getStrProp", String.class, Opcodes.ACC_PUBLIC).intercept(FixedValue.value(origBean.strProp))
                .defineMethod("setStrProp", Void.TYPE, Opcodes.ACC_PUBLIC).withParameters(String.class).intercept(MethodDelegation.to(new Intereptor(destBean, "strProp")));

        Class<?> dynamicType =     dynamic.make()
                .load(M1.class.getClassLoader())
                .getLoaded();


            Object readerObject = dynamicType.newInstance();
            Object writterObject = dynamicType.newInstance();


            BeanUtils.copyProperties(readerObject, writterObject);
            System.out.println("Out property:" + destBean.property);
            System.out.println("Out strProp:" + destBean.property);
    }



}

10

Project Lombok fournit des annotations @Getter et @Setter qui peuvent être utilisées au niveau de la classe pour générer automatiquement des méthodes getter et setter.

Lombok a également la capacité de générer des méthodes d'égalité et de hachage.

Ou vous pouvez utiliser ce @Dataqui est conforme au site Web de lombok:

@Data All together now: Un raccourci pour @ToString, @EqualsAndHashCode, @Getter sur tous les champs, @Setter sur tous les champs non finaux et @RequiredArgsConstructor!

@Data
public class BigGeneratedPojo {
  public String firstField;
  public int secondField;
  ...
  public ComplexObject nthField;
}

1
Lombok est facile à utiliser et rapide à installer. C'est une bonne solution.
Hayes Roach

Je pense que pour un raccourci, l'implémentation facile résoudra le problème et vous donnera également une grande lisibilité
leonardo rey

4

Compte tenu de vos contraintes, j'ajouterais une autre étape de génération de code. Comment mettre en œuvre dépend exactement de votre système de construction (Maven / Gradle / autre chose), mais JavaParser ou Roaster vous permettra d'analyser BigGeneratedPojo.javaet de créer une sous - classe avec les getters / setters souhaités, et le système de construction devrait mettre à jour automatiquement si des BigGeneratedPojochangements.


1

Les IDE comme Eclipse et STS fournissent une option pour ajouter des méthodes getters / setters. nous pouvons utiliser ces options pour créer des méthodes setters / getters


Le problème n'est pas d'écrire les méthodes réelles. Je sais comment les générer rapidement dans intellij. Le problème vient du fait qu'il BigGeneratedPojo s'agit d'un fichier généré, donc pour le manipuler, je devrais le sous-classer et avoir une classe wrapper avec ~ 120 méthodes factices (60 getters / setters) et c'est un cauchemar à maintenir.
Anthony

1
@Anthony Lorsque vous ouvrez le fichier dans l'éditeur de l'EDI, peu importe que le fichier ait été généré ou écrit manuellement. Dans les deux cas, vous pouvez ajouter les méthodes en une seule action. Ce n'est que lorsque vous prévoyez de recréer le fichier, que cela ne fonctionnera pas. Mais alors, avoir une classe avec 60 propriétés potentiellement changeantes est déjà un «cauchemar à entretenir».
Holger

1

Je suggérerais d'utiliser la réflexion pour obtenir les champs publics de votre classe et créer les getters et setters en utilisant votre propre programme Java comme suit. Considérez la classe suivante comme exemple.

import java.lang.reflect.Field;
import java.util.Arrays;

class TestClass {

    private int value;
    private String name;
    private boolean flag;
}

public class GetterSetterGenerator {

    public static void main(String[] args) {
        try {
            GetterSetterGenerator gt = new GetterSetterGenerator();
            StringBuffer sb = new StringBuffer();

            Class<?> c = Class.forName("TestClass");
            // Getting fields of the class
            Field[] fields = c.getDeclaredFields();

            for (Field f : fields) {
                String fieldName = f.getName();
                String fieldType = f.getType().getSimpleName();

                gt.createSetter(fieldName, fieldType, sb);
                gt.createGetter(fieldName, fieldType, sb);
            }
            System.out.println("" + sb.toString());

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void createSetter(String fieldName, String fieldType, StringBuffer setter) {
        setter.append("public void").append(" set");
        setter.append(getFieldName(fieldName));
        setter.append("(" + fieldType + " " + fieldName + ") {");
        setter.append("\n\t this." + fieldName + " = " + fieldName + ";");
        setter.append("\n" + "}" + "\n");
    }

    private void createGetter(String fieldName, String fieldType, StringBuffer getter) {
        // for boolean field method starts with "is" otherwise with "get"
        getter.append("public " + fieldType).append((fieldType.equals("boolean") ? " is" : " get") + getFieldName(fieldName) + " () { ");
        getter.append("\n\treturn " + fieldName + ";");
        getter.append("\n" + "}" + "\n");
    }

    private String getFieldName(String fieldName) {
        return fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, fieldName.length());
    }
}

Le code est tiré d'ici - légèrement modifié pour éviter inutile System.out. Vous pouvez facilement créer un fichier à partir de votre mainfonction et y placer vos getters et setters.

Vous pouvez également vérifier le programme en l' exécutant ici . J'espère que ça aide.


1

Vous pouvez utiliser Lombok. Est facile à utiliser et à mettre en œuvre. Il créera des getters et setter dans la compilation de fichiers .class. Il garde également le code plus propre.

@Getter @Setter @NoArgsConstructor
public class User implements Serializable {
 private @Id Long id;

private String firstName;
private String lastName;
private int age;

public User(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
}

}

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.