POJO'da genel mülkler için alıcıları / ayarlayıcıları çoğaltma yolu


9

~ 60 özellikli otomatik olarak oluşturulan bir POJO'muz var. Bu, alıcıları / ayarlayıcıları içermeyen avro 1.4 ile oluşturulur.

Nesneler arasında basit dönüşümler sağlamak için kullandığımız kitaplık, düzgün çalışması için alıcı / ayarlayıcı benzeri yöntemler gerektirir.

POJO'yu manuel olarak geçersiz kılmak ve tüm alıcıları / ayarlayıcıları manuel olarak oluşturmak zorunda kalmadan alıcıları / ayarlayıcıları çoğaltmanın bir yolu var mı?

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

arzu, şuna benzer bir kod yazmaktır:

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

Yanıtlar:


7

Proxy çekirdeklerini dinamik olarak, örneğin BitBuddy kullanarak oluşturmayı deneyebilirsiniz: https://bytebuddy.net/

Aşağıdaki örnek, bir yöntemin özellik alanının nasıl proxy yapılacağını gösterir. Bu sadece bir örnek olduğunu unutmayın ve muhtemelen sarmak ve yansımaları kullanarak bazı dinamik eklemek zorunda kalabilirsiniz, ancak dinamik olarak kodu genişletmek istiyorsanız oldukça ilginç bir seçenek olduğunu düşünüyorum.

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 Projesi , otomatik olarak alıcı ve ayarlayıcı yöntemleri oluşturmak için sınıf düzeyinde kullanılabilen @Getter ve @Setter ek açıklamaları sağlar.

Lombok aynı zamanda eşit ve karma kod üretme yeteneğine de sahiptir.

Veya @Datalombok web sitesine göre olanı kullanabilirsiniz :

@Data Şimdi hep birlikte: @ToString, @EqualsAndHashCode, @Tüm alanlarda @Getter, tüm final olmayan alanlarda Setter ve @RequiredArgsConstructor!

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

1
Lombok, kullanımı kolay ve kurulumu hızlıdır. Bu iyi bir çözüm.
Hayes Roach

Kısayol için kolay uygulama olduğunu düşünüyorum, sorunu çözecek ve ayrıca yüksek bir okunabilirlik sağlayacaktır
leonardo rey

4

Kısıtlamalarınız göz önüne alındığında, başka bir kod oluşturma adımı eklerdim. Tam olarak nasıl uygulanacağı derleme sisteminize (Maven / Gradle / başka bir şey) bağlıdır, ancak JavaParser veya RoasterBigGeneratedPojo.java istenen alıcılar / ayarlayıcılarla bir alt sınıfı ayrıştırmanıza ve oluşturmanıza izin verir ve derleme sistemi BigGeneratedPojodeğişiklik olursa otomatik olarak güncellemelidir .


1

Eclipse ve STS gibi IDE'ler getters / setters yöntemleri ekleme seçeneği sunar. ayarlayıcılar / alıcılar yöntemleri oluşturmak için bu seçeneği kullanabiliriz


Sorun gerçek yöntemleri yazmıyor. Bunları intellij'de nasıl hızlıca üreteceğimizi biliyorum. Sorun BigGeneratedPojo , oluşturulan bir dosya olduğu gerçeğinde ortaya çıkar , bu yüzden gerçekten manipüle etmek için onu alt sınıflara ayırmak ve w / ~ 120 kukla yöntemleri (60 alıcılar / ayarlayıcılar) olan bir sarıcı sınıfına sahip olmalıyım ve sürdürmek için bir kabus.
Anthony

1
@Anthony Dosyayı IDE'nin düzenleyicisinde açtığınızda, dosyanın el ile oluşturulması veya yazılması önemli değildir. Her iki durumda da, yöntemleri tek bir eylemle ekleyebilirsiniz. Yalnızca dosyayı yeniden oluşturmayı planladığınızda çalışmaz. Ancak daha sonra, potansiyel olarak değişen 60 özelliğe sahip bir sınıfa sahip olmak zaten “sürdürmek için bir kabus” dur.
Holger

1

Sınıfınızdan ortak alanları almak ve aşağıdaki gibi kendi Java programınızı kullanarak alıcıları ve ayarlayıcıları oluşturmak için yansıma kullanmanızı öneririm. Örnek olarak aşağıdaki sınıfı ele alalım.

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

Kod buradan alınır - gereksiz önlemek için biraz değiştirilir System.out. mainİşlevinizden kolayca bir dosya oluşturabilir ve alıcılarınızı ve ayarlayıcılarınızı oraya koyabilirsiniz.

Programı burada çalıştırarak da kontrol edebilirsiniz . Umarım bu yardımcı olur.


1

Lombok'u kullanabilirsiniz. Kullanımı ve uygulaması kolaydır. .Class dosyası sonrası derlemesinde getters ve setter oluşturur. Ayrıca kodun daha temiz görünmesini sağlar.

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

}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.