byte-buddy icon indicating copy to clipboard operation
byte-buddy copied to clipboard

How to add setter method for uncertain class fields ?

Open thisisgpy opened this issue 3 years ago • 1 comments

According to the official example, generating getter/setter methods for a Field can be done by implementing an interface. But this is when I know exactly which Fields will be added to the Class. My program gets the fields to be added from the database every time, so I need to add getter/setter methods for these fields at runtime.

I define a getter method and assign it as FieldAccessor using intercept method. When I define a setter method in the same way and specify it as FieldAccessor, the system throws an exception.

Here is a simplified example code:

@Data
public class MappingFieldBO {
    private String fieldName;
    private int maxScore;
    public MappingFieldBO() {
    }
    public MappingFieldBO(String fieldName, int maxScore) {
        this.fieldName = fieldName;
        this.maxScore = maxScore;
    }
}

@Data
public class BaseMappingFieldBO {
    private Long id;
}
public class Main {
    public static void main(String[] args) {
        List<MappingFieldBO> mappingFields = getFromDB();
        DynamicType.Builder<BaseMappingFieldBO> builder = new ByteBuddy()
                .subclass(BaseMappingFieldBO.class)
                .name("io.buyan.dv.console.MappingBean");
         // add uncertain fields to class
        for (MappingFieldBO mappingField : mappingFields) {
            String fieldName = mappingField.getFieldName();
            builder = builder.defineField(fieldName, String.class, Visibility.PUBLIC)
                    // define getter method
                    .defineMethod(getterName(fieldName), String.class, Visibility.PUBLIC)
                    .intercept(FieldAccessor.ofField(fieldName))
                    // define setter method
                    // throw IllegalArgumentException: Method public void io.buyan.dv.console.MappingBean.setShipping() is no bean accessor
                    .defineMethod(setterName(fieldName), Void.TYPE, Visibility.PUBLIC)
                    .intercept(FieldAccessor.ofField(fieldName));
        }
        Class<? extends BaseMappingFieldBO> clazz = builder.make().load(Thread.currentThread().getContextClassLoader()).getLoaded();
    }

    private static String setterName(String fieldName) {
        return "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }
    private static String getterName(String fieldName) {
        return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    private static List<MappingFieldBO> getFromDB() {
        List<MappingFieldBO> list = new ArrayList<>();
        list.add(new MappingFieldBO("shipping", 10));
        list.add(new MappingFieldBO("deduct", 8));
        return list;
    }
}

thisisgpy avatar May 16 '22 13:05 thisisgpy

You could just use builder.defineProperty("foo", String.class) what will create corresponding setters or getters. The idea with the interface is meant for cases where you want to set a specific field, for example to add some state to a proxy.

raphw avatar May 16 '22 19:05 raphw