在POJO中复制公共属性的获取者/设置者的方法


9

我们有一个自动生成的POJO,具有约60个属性。这是通过avro 1.4生成的,其中不包括getter / setter。

我们用来在对象之间提供简单转换的库需要使用类似于getter / setter的方法才能正常工作。

有没有一种方法可以复制获取器/设置器,而不必手动覆盖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");

Answers:


7

您可以尝试例如使用BitBuddy动态生成代理Bean: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批注,可在类级别使用它们自动生成getter和setter方法。

Lombok还具有生成equals和hashcode方法的能力。

或者,您可以@Data根据lombok网站使用:

@Data现在在一起:@ ToString,@ EqualsAndHashCode,所有字段上的@Getter,所有非最终字段上的@Setter以及@RequiredArgsConstructor的快捷方式!

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

1
龙目岛易于使用且设置迅速。这是一个很好的解决方案。
Hayes Roach,

我认为,捷径是简单的实现,可以解决问题并提高可读性
莱昂纳多·雷

4

鉴于您的限制,我将添加另一个代码生成步骤。究竟如何实现它完全取决于您的构建系统(Maven / Gradle /其他),但是JavaParserRoaster允许您解析BigGeneratedPojo.java并创建具有所需getter / setter的子类,并且构建系统应该在BigGeneratedPojo发生更改时自动对其进行更新。


1

像Eclipse和STS这样的IDE提供了添加getters / setters方法的选项。我们可以使用这些选项来创建setters / getters方法


问题不在于编写实际方法。我知道如何在intellij中快速生成那些。问题出在一个事实,BigGeneratedPojo 即生成的文件上,因此,要实际操作它,我需要将其子类化,并具有w /〜120个伪方法(60个getters / setters)的包装类,这是维护噩梦的过程。
安东尼

1
@Anthony在IDE的编辑器中打开文件时,文件是手动生成还是无关紧要。无论哪种情况,都可以通过一个操作添加方法。仅当您计划重新生成文件时,它才起作用。但是,拥有60个可能会更改的属性的类已经是“一场噩梦”。
Holger

1

我建议使用反射从类中获取公共字段,并使用自己的Java程序创建getter和setter,如下所示。以下面的类为例。

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。它还可以使代码看起来更干净。

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

}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.