Möglichkeit, Getter / Setter für öffentliche Eigenschaften in einem POJO zu replizieren

9

Wir haben ein POJO, das automatisch mit ~ 60 Eigenschaften generiert wird. Dies wird mit avro 1.4 generiert, das keine Getter / Setter enthält.

Eine Bibliothek, mit der wir einfache Transformationen zwischen Objekten bereitstellen, erfordert Getter / Setter-ähnliche Methoden, um ordnungsgemäß zu funktionieren.

Gibt es eine Möglichkeit, Getter / Setter zu replizieren, ohne das POJO manuell überschreiben und alle Getter / Setter manuell erstellen zu müssen?

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

Der Wunsch ist es, Code zu schreiben, der aussieht wie:

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");
Anthony
quelle

Antworten:

7

Sie können versuchen, die Proxy-Beans dynamisch zu generieren, z. B. mithilfe von BitBuddy: https://bytebuddy.net/

Das folgende Beispiel zeigt, wie ein Eigenschaftsfeld einer Methode als Proxy verwendet wird. Beachten Sie, dass dies nur ein Beispiel ist und dass Sie es höchstwahrscheinlich umbrechen und mithilfe von Reflexionen etwas Dynamik hinzufügen müssen. Ich denke jedoch, dass dies eine interessante Option ist, wenn Sie Code dynamisch erweitern möchten.

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



}
Vizekönig
quelle
10

Project Lombok bietet @ Getter- und @ Setter-Annotationen, die auf Klassenebene verwendet werden können, um Getter- und Setter-Methoden automatisch zu generieren.

Lombok kann auch Gleichheits- und Hashcode-Methoden generieren.

Oder Sie können @Datadie laut lombok Website verwenden:

@Data Jetzt alle zusammen: Eine Verknüpfung für @ToString, @EqualsAndHashCode, @Getter für alle Felder, @Setter für alle nicht endgültigen Felder und @RequiredArgsConstructor!

@Data
public class BigGeneratedPojo {
  public String firstField;
  public int secondField;
  ...
  public ComplexObject nthField;
}
Karthik Rao
quelle
1
Lombok ist einfach zu bedienen und schnell einzurichten. Dies ist eine gute Lösung.
Hayes Roach
Ich denke für eine Abkürzung ist die einfache Implementierung, wird das Problem lösen und Ihnen auch eine hohe Lesbarkeit geben
Leonardo Rey
4

Angesichts Ihrer Einschränkungen würde ich einen weiteren Schritt zur Codegenerierung hinzufügen. Wie Sie es genau implementieren, hängt von Ihrem Build-System ab (Maven / Gradle / etwas anderes). Mit JavaParser oder Roaster können Sie jedoch BigGeneratedPojo.javaeine Unterklasse mit den gewünschten Gettern / Setzern analysieren und erstellen. Das Build-System sollte sie bei BigGeneratedPojoÄnderungen automatisch aktualisieren .

Alexey Romanov
quelle
1

IDEs wie Eclipse und STS bieten die Möglichkeit, Getter / Setter-Methoden hinzuzufügen. Mit dieser Option können wir Setter / Getter-Methoden erstellen

Durai Kasinathan
quelle
Das Problem besteht nicht darin, die tatsächlichen Methoden zu schreiben. Ich weiß, wie man diese in Intellij schnell generiert. Das Problem tritt in der Tatsache auf, dass BigGeneratedPojo es sich um eine generierte Datei handelt. Um sie tatsächlich zu manipulieren, müsste ich sie in eine Unterklasse unterteilen und eine Wrapper-Klasse mit ~ 120 Dummy-Methoden (60 Getter / Setter) haben. Dies ist ein Albtraum, den es zu pflegen gilt.
Anthony
1
@Anthony Wenn Sie die Datei im Editor der IDE öffnen, spielt es keine Rolle, ob die Datei manuell generiert oder geschrieben wurde. In beiden Fällen können Sie die Methoden mit einer einzigen Aktion hinzufügen. Nur wenn Sie vorhaben, die Datei neu zu generieren, funktioniert sie nicht. Aber eine Klasse mit 60 potenziell sich ändernden Eigenschaften zu haben, ist bereits ein „Albtraum, den man aufrechterhalten muss“.
Holger
1

Ich würde vorschlagen, Reflection zu verwenden, um die öffentlichen Felder aus Ihrer Klasse abzurufen und die Getter und Setter mit Ihrem eigenen Java-Programm wie folgt zu erstellen. Betrachten Sie die folgende Klasse als Beispiel.

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());
    }
}

Der Code stammt von hier - leicht modifiziert, um unnötige zu vermeiden System.out. Sie können ganz einfach eine Datei aus Ihrer mainFunktion erstellen und Ihre Getter und Setter dort ablegen.

Sie können das Programm überprüfen, indem Sie es auch hier ausführen. Ich hoffe das hilft.

Reaz Murshed
quelle
1

Sie können Lombok verwenden. Es ist einfach zu bedienen und zu implementieren. Es werden Getter und Setter in der .class-Datei nach der Kompilierung erstellt. Außerdem sieht der Code sauberer aus.

@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;
}

}}

CodeRider
quelle