Cara untuk mereplikasi getter / setter untuk properti publik di POJO

9

Kami memiliki POJO yang dihasilkan secara otomatis dengan ~ 60 properti. Ini dihasilkan dengan avro 1.4, yang tidak termasuk getter / setter.

Pustaka yang kami gunakan untuk menyediakan transformasi sederhana antara objek membutuhkan metode pengambil / penyetel seperti untuk bekerja dengan benar.

Apakah ada cara untuk mereplikasi getter / setter tanpa harus mengesampingkan POJO secara manual dan membuat semua getter / setter secara manual?

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

keinginannya adalah menulis kode yang terlihat seperti:

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
sumber

Jawaban:

7

Anda dapat mencoba membuat kacang proksi secara dinamis, misalnya, menggunakan BitBuddy: https://bytebuddy.net/

Sampel di bawah ini menunjukkan cara membuat proxy bidang properti metode. Perhatikan bahwa ini hanya sampel, dan kemungkinan besar Anda mungkin harus membungkusnya dan menambahkan beberapa dinamis menggunakan refleksi, tapi saya pikir ini pilihan yang cukup menarik jika Anda ingin memperluas kode secara dinamis.

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



}
Vicctor
sumber
10

Project Lombok menyediakan anotasi @Getter dan @Setter yang dapat digunakan di tingkat kelas untuk menghasilkan metode pengambil dan penyetel secara otomatis.

Lombok juga memiliki kemampuan untuk menghasilkan metode equals dan hashcode.

Atau Anda bisa menggunakan @Datayang sesuai dengan situs web lombok:

@ Data Semua bersama-sama sekarang: Pintasan untuk @ToString, @EqualsAndHashCode, @Getter di semua bidang, @Setter di semua bidang non-final, dan @RequiredArgsConstructor!

@Data
public class BigGeneratedPojo {
  public String firstField;
  public int secondField;
  ...
  public ComplexObject nthField;
}
Karthik Rao
sumber
1
Lombok mudah digunakan dan cepat diatur. Ini solusi yang bagus.
Hayes Roach
Saya pikir untuk jalan pintas adalah implementasi yang mudah, akan menyelesaikan masalah dan juga memberi Anda keterbacaan yang tinggi
leonardo rey
4

Mengingat kendala Anda, saya akan menambahkan langkah pembuatan kode lain. Cara mengimplementasikannya tergantung pada sistem build Anda (Maven / Gradle / sesuatu yang lain), tetapi JavaParser atau Roaster akan memungkinkan Anda untuk mem BigGeneratedPojo.java- parsing dan membuat subkelas dengan getter / setter yang diinginkan, dan sistem build harus secara otomatis memperbaruinya jika ada BigGeneratedPojoperubahan.

Alexey Romanov
sumber
1

IDE seperti Eclipse dan STS menyediakan opsi untuk menambahkan metode getter / setter. kita bisa menggunakan opsi itu untuk membuat metode setter / getter

Durai Kasinathan
sumber
Masalahnya bukan menulis metode yang sebenarnya. Saya tahu cara membuat mereka dengan cepat di Intellij. Masalahnya muncul pada kenyataan bahwa itu BigGeneratedPojo adalah file yang dihasilkan, jadi untuk benar-benar memanipulasinya, saya perlu mensubclassnya dan memiliki kelas wrapper dengan metode dummy ~ 120 (60 getter / setter) dan merupakan mimpi buruk untuk dipertahankan.
Anthony
1
@Anthony Ketika Anda membuka file di editor IDE, itu tidak relevan apakah file telah dibuat atau ditulis secara manual. Dalam kedua kasus tersebut, Anda dapat menambahkan metode dengan satu tindakan. Hanya ketika Anda berencana untuk membuat ulang file, itu tidak akan berfungsi. Tapi kemudian, memiliki kelas dengan 60 properti yang berpotensi berubah sudah merupakan "mimpi buruk untuk dipertahankan".
Holger
1

Saya akan menyarankan menggunakan refleksi untuk mendapatkan bidang publik dari kelas Anda dan membuat getter dan setter menggunakan program Java Anda sendiri sebagai berikut. Pertimbangkan kelas berikut sebagai contoh.

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

Kode diambil dari sini - sedikit dimodifikasi untuk menghindari yang tidak perlu System.out. Anda dapat dengan mudah membuat file dari mainfungsi Anda dan meletakkan getter dan setter Anda di sana.

Anda dapat memeriksa program dengan menjalankannya di sini juga. Saya harap itu membantu.

Reaz Murshed
sumber
1

Anda bisa menggunakan Lombok. Apakah mudah digunakan dan diimplementasikan. Ini akan membuat getter dan setter dalam kompilasi postingan file .class. Itu juga membuat kode terlihat bersih.

@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
sumber