Спосіб копіювання геттерів / сеттерів для публічних властивостей у POJO


9

У нас є POJO, який автоматично генерується з ~ 60 властивостями. Це генерується з avro 1.4, який не включає геттерів / сеттерів.

Для належної роботи бібліотеки, яку ми використовуємо для забезпечення простих перетворень між об'єктами, потрібні методи, подібні до геттера / сеттера.

Чи є спосіб копіювання геттерів / сеттерів без необхідності вручну змінювати POJO та створювати всі геттери / сетери вручну?

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

бажання - написати код, який виглядає так:

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

Відповіді:


7

Ви можете спробувати генерувати проксі-файли динамічно, наприклад, використовуючи BitBuddy: https://bytebuddy.net/

Наведений нижче приклад показує, як проксити властивість поля методу. Зауважте, що це лише зразок, і, швидше за все, вам, можливо, доведеться обернути його та додати деяку динаміку за допомогою роздумів, але я думаю, що це досить цікавий варіант, якщо ви бажаєте динамічно розширити код.

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

Проект Lombok надає анотації @Getter та @Setter, які можна використовувати на рівні класу для автоматичного генерування методів геттера та сеттера.

Lombok також має можливість генерувати рівні та методи хеш-коду.

Або ви можете використовувати те, @Dataщо є на веб-сайті lombok:

@Data Усі разом зараз: ярлик для @ToString, @EqualsAndHashCode, @Getter на всіх полях, @Setter на всіх не остаточних полях і @RequiredArgsConstructor!

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

1
Lombok простий у використанні та швидкий у налаштуванні. Це хороше рішення.
Hayes Roach

Я думаю, що скорочення - це проста реалізація, вирішить проблему, а також дасть вам високу читабельність
leonardo rey

4

Враховуючи ваші обмеження, я б додав ще один крок створення коду. Як реалізувати це в точності залежить від побудови системи (Maven / Gradle / що - то ще), але JavaParser або жаровня дозволить розібрати BigGeneratedPojo.javaі створити підклас з необхідними геттери / сеттери, і система збирання повинна автоматично оновлювати його , якщо BigGeneratedPojoзміни.


1

IDE, такі як Eclipse та STS, надають можливість додавання методів getters / setters. ми можемо використовувати цю опцію для створення методів setters / getters


Питання полягає не в написанні власне методів. Я знаю, як швидко їх генерувати в Intellij. Проблема виникає у тому, що BigGeneratedPojo це генерований файл, тому для того, щоб фактично маніпулювати ним, мені потрібно було б підкласифікувати його та мати клас обгортки w / ~ 120 фіктивних методів (60 геттерів / сеттерів) і це кошмар для підтримки.
Антоній

1
@Anthony Коли ви відкриваєте файл у редакторі IDE, не має значення, генерувався чи писався файл вручну. В будь-якому випадку ви можете додати методи однією дією. Тільки коли ви плануєте повторно генерувати файл, він не працюватиме. Але тоді мати клас із 60 потенційно зміненими властивостями - це вже «кошмар, який потрібно підтримувати».
Холгер

1

Я б запропонував за допомогою роздумів отримати загальнодоступні поля з вашого класу та створити геттери та сетери за допомогою власної програми Java наступним чином. Розглянемо наступний клас як приклад.

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

Код взято звідси - трохи змінений, щоб уникнути зайвого System.out. Ви можете легко створити файл зі своєї mainфункції та помістити туди ваші геттери та сетери.

Ви можете перевірити програму, запустивши її і тут . Я сподіваюся, що це допомагає.


1

Можна використовувати Ломбок. Простий у використанні та реалізації. Це створить геттерів та сеттерів у компіляції файлів .class. Він також забезпечує вигляд коду чистішим.

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

}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.