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