Maneira de replicar getters / setters para propriedades públicas em um POJO

9

Temos um POJO gerado automaticamente com ~ 60 propriedades. Isso é gerado com o avro 1.4, que não inclui getters / setters.

Uma biblioteca que usamos para fornecer transformações simples entre objetos requer métodos do tipo getter / setter para funcionar corretamente.

Existe uma maneira de replicar getters / setters sem ter que substituir manualmente o POJO e criar todos os getters / setters manualmente?

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

o desejo é escrever um código parecido com:

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
fonte

Respostas:

7

Você pode tentar gerar os beans proxy dinamicamente, por exemplo, usando o BitBuddy: https://bytebuddy.net/

A amostra abaixo mostra como proxy de um campo de propriedade de um método. Observe que este é apenas um exemplo e, provavelmente, você precisará envolvê-lo e adicionar algumas dinâmicas usando reflexões, mas acho que é uma opção bastante interessante se você deseja estender o código dinamicamente.

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
fonte
10

O Projeto Lombok fornece anotações @Getter e @Setter que podem ser usadas no nível da classe para gerar métodos getter e setter automaticamente.

O Lombok também tem a capacidade de gerar métodos iguais e hashcode.

Ou você pode usar o @Dataque está de acordo com o site lombok:

@Data Todos juntos agora: um atalho para @ToString, @EqualsAndHashCode, @Getter em todos os campos, @Setter em todos os campos não finais e @RequiredArgsConstructor!

@Data
public class BigGeneratedPojo {
  public String firstField;
  public int secondField;
  ...
  public ComplexObject nthField;
}
Karthik Rao
fonte
11
Lombok é fácil de usar e rápido de configurar. Esta é uma boa solução.
Hayes Roach
Eu acho que por um curto-corte é a implementação fácil, vai resolver o problema e também dar-lhe uma alta legibilidade
leonardo rey
4

Dadas as suas restrições, eu adicionaria outra etapa de geração de código. Como implementá-lo depende exatamente do seu sistema de compilação (Maven / Gradle / outra coisa), mas o JavaParser ou Roaster permitirá analisar BigGeneratedPojo.javae criar uma subclasse com os getters / setters desejados, e o sistema de compilação deve atualizá-lo automaticamente se houver BigGeneratedPojoalterações.

Alexey Romanov
fonte
1

IDEs como Eclipse e STS oferecem opção para adicionar métodos getters / setters. podemos usar essas opções para criar métodos setters / getters

Durai Kasinathan
fonte
O problema não está escrevendo os métodos reais. Eu sei como gerar esses rapidamente em intellij. O problema surge no fato de BigGeneratedPojo ser um arquivo gerado, portanto, para realmente manipulá-lo, eu precisaria subclassificá-lo e ter uma classe de wrapper com métodos fictícios 120 ~ fictícios (60 getters / setters) e é um pesadelo para manter.
15133 Anthony
11
@ Anthony Quando você abre o arquivo no editor do IDE, é irrelevante se o arquivo foi gerado ou gravado manualmente. Em qualquer um dos casos, você pode adicionar os métodos com uma única ação. Somente quando você planeja gerar novamente o arquivo, ele não funcionará. Mas então, ter uma classe com 60 propriedades em potencial de mudança já é "um pesadelo para manter".
Holger
1

Eu sugeriria o uso de reflexão para obter os campos públicos de sua classe e criar os getters e setters usando seu próprio programa Java da seguinte maneira. Considere a seguinte classe como um exemplo.

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

O código é retirado daqui - ligeiramente modificado para evitar desnecessários System.out. Você pode facilmente criar um arquivo a partir de sua mainfunção e colocar seus getters e setters lá.

Você pode verificar o programa executando-o aqui também. Espero que ajude.

Reaz Murshed
fonte
1

Você pode usar Lombok. É fácil de usar e implementar. Ele criará getters e setter na compilação de postagem de arquivo .class. Também mantém o código mais limpo.

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