Java 如何引用当前使用Byte Buddy定义的类中的字段?
我试图使用Byte Buddy将JSON模式编译成JavaBeans。我已经让类、字段和getter/setter生成工作。我也想生成toString/equals/hashCode,但这样做似乎需要为我正在定义的类中的字段获取一个Java 如何引用当前使用Byte Buddy定义的类中的字段?,java,byte-buddy,Java,Byte Buddy,我试图使用Byte Buddy将JSON模式编译成JavaBeans。我已经让类、字段和getter/setter生成工作。我也想生成toString/equals/hashCode,但这样做似乎需要为我正在定义的类中的字段获取一个FieldDescription,我看不到任何方法。可能吗?还是我完全走错了路 我的代码的基本部分: public Class<?> createClass(final String className) { DynamicType.Builder
FieldDescription
,我看不到任何方法。可能吗?还是我完全走错了路
我的代码的基本部分:
public Class<?> createClass(final String className) {
DynamicType.Builder<Object> builder = new ByteBuddy()
.subclass(Object.class)
.name(className);
// create fields and accessor methods
for(final Map.Entry<String, Type> field : this.fields.entrySet()) {
final String fieldName = field.getKey();
Type fieldValue = field.getValue();
if (fieldValue instanceof ClassDescription) {
// recursively generate classes as needed
fieldValue = ((ClassDescription) fieldValue).createClass(fieldName);
}
builder = builder
// field
.defineField(fieldName, fieldValue, Visibility.PRIVATE);
// getter
.defineMethod(getterName(fieldName), fieldValue, Visibility.PUBLIC)
.intercept(FieldAccessor.ofBeanProperty())
// setter
.defineMethod(setterName(fieldName), Void.TYPE, Visibility.PUBLIC)
.withParameter(fieldValue)
.intercept(FieldAccessor.ofBeanProperty());
}
// TODO: Create toString/hashCode/equals
// builder = builder
// .defineMethod("toString", String.class, Visibility.PUBLIC)
// .intercept(new ToStringImplementation(fieldDescriptions));
final Class<?> type = builder
.make()
.load(this.getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
return type;
}
public Implementation makeToString(final LinkedHashMap<String, FieldDescription> fields) {
final ArrayList<StackManipulation> ops = new ArrayList<>();
try {
final TypeDescription stringBuilderDesc = new TypeDescription.ForLoadedType(StringBuilder.class);
final MethodDescription sbAppend = new MethodDescription.ForLoadedMethod(
StringBuilder.class.getDeclaredMethod("append", Object.class));
final MethodDescription sbToString = new MethodDescription.ForLoadedMethod(
StringBuilder.class.getDeclaredMethod("toString"));
// create the StringBuilder
ops.add(MethodInvocation.invoke(
new MethodDescription.ForLoadedConstructor(StringBuilder.class.getConstructor()))
);
// StringBuilder::append returns the StringBuilder, so we don't need to
// save the reference returned from the 'new'
for(final Map.Entry<String, FieldDescription> field : fields.entrySet()) {
ops.add(FieldAccess.forField(field.getValue()).read());
ops.add(MethodInvocation.invoke(sbAppend));
}
// call StringBuilder::toString
ops.add(MethodInvocation.invoke(sbToString).virtual(stringBuilderDesc));
// return the toString value
ops.add(MethodReturn.of(TypeDescription.STRING));
} catch (final NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
return new Implementation.Simple(ops.toArray(EMPTY_STACKMANIPULATION_ARRAY));
}
public类createClass(最终字符串类名){
DynamicType.Builder=newbytebuddy()
.subclass(Object.class)
.名称(类名);
//创建字段和访问器方法
for(最终Map.Entry字段:this.fields.entrySet()){
最后一个字符串fieldName=field.getKey();
类型fieldValue=field.getValue();
if(类描述的字段值实例){
//根据需要递归生成类
fieldValue=((ClassDescription)fieldValue).createClass(fieldName);
}
建筑商
//场
.defineField(字段名、字段值、可见性.PRIVATE);
//吸气剂
.defineMethod(getterName(fieldName)、fieldValue、Visibility.PUBLIC)
.intercept(FieldAccessor.ofBeanProperty())
//塞特
.defineMethod(setterName(字段名)、Void.TYPE、Visibility.PUBLIC)
.withParameter(字段值)
.intercept(FieldAccessor.ofBeanProperty());
}
//TODO:创建toString/hashCode/equals
//建筑商
//.defineMethod(“toString”,String.class,Visibility.PUBLIC)
//.截取(新ToString实施(字段描述));
最终类类型=生成器
.make()
.load(此.getClass().getClassLoader(),ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
返回类型;
}
公共实现makeToString(最终LinkedHashMap字段){
最终ArrayList ops=新ArrayList();
试一试{
final TypeDescription stringBuilderDesc=新的TypeDescription.ForLoadedType(StringBuilder.class);
final MethodDescription sbAppend=新MethodDescription.ForLoadedMethod(
StringBuilder.class.getDeclaredMethod(“append”,Object.class));
final MethodDescription sbToString=新MethodDescription.ForLoadedMethod(
StringBuilder.class.getDeclaredMethod(“toString”);
//创建StringBuilder
添加(MethodInvocation.invoke(
新的MethodDescription.ForLoadedConstructor(StringBuilder.class.getConstructor())
);
//追加返回StringBuilder,因此我们不需要
//保存从“新建”返回的引用
for(最终映射.Entry字段:fields.entrySet()){
add(FieldAccess.forField(field.getValue()).read());
ops.add(MethodInvocation.invoke(sbAppend));
}
//调用StringBuilder::toString
add(MethodInvocation.invoke(sbToString.virtual)(stringBuilderDesc));
//返回toString值
add(MethodReturn.of(TypeDescription.STRING));
}捕获(最终NoSuchMethodException | SecurityException e){
抛出新的运行时异常(e);
}
返回新的Implementation.Simple(ops.toArray(EMPTY_STACKMANIPULATION_ARRAY));
}
我提出了一个基于的解决方案。这是一个简化的版本
本例将采用一个类,该类包含一个名为name
的字符串字段,并生成一个toString
,其结果类似于MyGeneratedClass[name=Tom]
public static class ToStringImplementation implements Implementation {
public static final TypeDescription SB_TYPE;
public static final MethodDescription SB_CONSTRUCTOR_DEFAULT;
public static final MethodDescription SB_APPEND_STRING;
public static final MethodDescription SB_TO_STRING;
static {
try {
SB_TYPE = new TypeDescription.ForLoadedType(StringBuilder.class);
SB_CONSTRUCTOR_DEFAULT = new MethodDescription.ForLoadedConstructor(StringBuilder.class.getConstructor());
SB_APPEND_STRING = new MethodDescription.ForLoadedMethod(StringBuilder.class.getDeclaredMethod("append", String.class));
SB_TO_STRING = new MethodDescription.ForLoadedMethod(StringBuilder.class.getDeclaredMethod("toString"));
}
catch (final NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
@Override
public InstrumentedType prepare(final InstrumentedType instrumentedType) {
return instrumentedType;
}
@Override
public ByteCodeAppender appender(final Target implementationTarget) {
final TypeDescription thisType = implementationTarget.getInstrumentedType();
return new ByteCodeAppender.Simple(Arrays.asList(
// allocate the StringBuilder
TypeCreation.of(SB_TYPE),
// constructor doesn't return a reference to the object, so need to save a copy
Duplication.of(SB_TYPE),
// invoke the constructor
MethodInvocation.invoke(SB_CONSTRUCTOR_DEFAULT),
// opening portion of toString output
new TextConstant(thisType.getName() + "["),
MethodInvocation.invoke(SB_APPEND_STRING),
// field label
new TextConstant("name="),
MethodInvocation.invoke(SB_APPEND_STRING),
// field value
// virtual call first param is always "this" reference
MethodVariableAccess.loadThis(),
// first param to append is the field value
FieldAccess.forField(thisType.getDeclaredFields()
.filter(ElementMatchers.named("name"))
.getOnly()
).read(),
// invoke append(String), since name is a String-type field
MethodInvocation.invoke(SB_APPEND_STRING),
// closing portion of toString output
new TextConstant("]"),
MethodInvocation.invoke(SB_APPEND_STRING),
// call toString and return the result
MethodInvocation.invoke(SB_TO_STRING),
MethodReturn.of(TypeDescription.STRING)
));
}
}
像这样应用它
builder
.method(ElementMatchers.named("toString"))
.intercept(new ToStringImplementation());
你应该这样做。我认为有一天,将一个默认实现添加到字节好友。@ RavaelWelHalter:谢谢您的反馈。不过,还有一个问题:有没有办法解决字段类型的
StringBuilder::append
的最佳重载问题,而不是手动指定它?我一直在研究MethodDelegation
,但我看不到在这种情况下使用它的方法。在这种情况下,你应该将字节码编码出来,对于原语来说,这只是效率更高。是的,我并不是真的想要委托,我只是想找到某种方法来找到最具体的方法(如),对参数类型使用TypeDescription
s,这样我就可以在调用时发出它。