在 Byte Buddy 中动态设置 @FieldProxy 字段
Setting up @FieldProxy field dynamically in Byte Buddy
我必须实现一个可用于动态指定字段的拦截器,而不管字段名称如何。
关于此处答案的评论
我读过
you can really just use reflection on a @This object. As long as you
cache the Field instances, this has no relevance to performance.
但是我怀疑下面的拦截器实现是否有效(如果我理解正确的话)。
public static class DynamicFieldInterceptor {
private final String fieldName;
public DynamicFieldInterceptor(String fieldName) {
this.fieldName = fieldName;
}
public void intercept(@This Object thiz) throws NoSuchFieldException, IllegalAccessException {
Field field = thiz.getClass().getDeclaredField(fieldName);
boolean oldAccessible = field.isAccessible();
field.setAccessible(true);
Long fieldValue = (Long)field.get(thiz);
field.set(thiz, fieldValue + 1L); // !< Instead of my business logic
field.setAccessible(oldAccessible);
}
}
我还尝试了以下想法:为每个字段生成拦截器 classes,并在 @FieldProxy 参数上使用不同的注释。比使用生成的 class 作为目标 class.
的拦截器
public interface Metaclass {
void intercept(GetterAndSetter field);
}
public static class MetaclassInterceptor implements Metaclass{
@Override
public void intercept(GetterAndSetter field) {
field.set((Long)field.get() + 1L);
}
}
public static Class<?> annotateInterceptorClass(final String annotation)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return new ByteBuddy()
.subclass(MetaclassInterceptor.class)
.topLevelType()
.name("ClassForIntercepting_" + annotation + "_Field")
.modifiers(Visibility.PUBLIC, Ownership.STATIC)
.defineMethod("intercept", void.class, Visibility.PUBLIC)
.withParameter(GetterAndSetter.class, "intercept")
.annotateParameter(AnnotationDescription.Builder.ofType(FieldProxy.class)
.define("value", annotation).build())
.intercept(SuperMethodCall.INSTANCE)
.make()
.load(MetaclassInterceptor.class.getClassLoader())
.getLoaded();
}
class好像生成的不错。生成的class中的方法存在,并且参数被注解了预期的注解。
然而,当我尝试使用生成的 class 作为拦截器时,出现异常。
Class<?> klass = new ByteBuddy()
.subclass(Object.class)
.defineProperty("index0", Long.class, false)
.defineProperty("index1", Long.class, false)
.defineMethod("doSomeActions", void.class, Visibility.PUBLIC)
.intercept(
MethodDelegation
.withDefaultConfiguration()
.withBinders(FieldProxy.Binder.install(GetterAndSetter.class))
// Use dynamically generated interceptor, see abode
.to(annotateInterceptor("index0"))
.andThen(
MethodDelegation
.withDefaultConfiguration()
.withBinders(FieldProxy.Binder.install(GetterAndSetter.class))
// Use dynamically generated interceptor, see abode
.to(annotateInterceptor("index1"))
)
)
.make()
.load(MetaclassInterceptor.class.getClassLoader())
.getLoaded();
Exception in thread "main" java.lang.NoClassDefFoundError: LClassForIntercepting_index0_Field;
at java.base/java.lang.Class.getDeclaredFields0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3062)
at java.base/java.lang.Class.getDeclaredField(Class.java:2410)
at net.bytebuddy.implementation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:120)
at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:187)
at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:102)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:5662)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:5651)
at MainClass4.main(MainClass4.java:107)
即使我成功地动态实现了拦截器,我也确信这不是完美的方法。我认为必须有可能以更简单的方式进行。真的,@FieldProxy 注解可以从显式指定的名称和bean 中获取字段属性 如果注解中的字段名称没有指定,所以我认为这是一个技术机会,可以将它映射到任何其他字段。
当您使用 load(MetaclassInterceptor.class.getClassLoader())
加载 class 时,您正在创建一个新的 class 加载器,它不会对其他加载器上的任何其他 class 可见,除非你重复使用它。
您可以:
a) 将make
步骤创建的两个DynamicType
组合在一起加载。这样,它们将最终进入相同的 class 加载器。
b) 获取第一个生成的 class 的 class 加载程序并将其转换为 InjectionClassLoader
。您还需要指定 ClassLoadingStrategy.WRAPPER.opened()
并将其与 InjectionClassLoader.Strategy.INSTANCE
一起使用。请注意,这将允许任何引用您生成的 class 实例的人在同一包中定义 classes。
c) 使用 ClassLoadingStrategy.Default.INJECTION
在原始 class 加载程序中定义 classes 的内容,而不创建包装器。并不是说这个策略依赖于 Unsafe API.
我必须实现一个可用于动态指定字段的拦截器,而不管字段名称如何。
关于此处答案的评论
you can really just use reflection on a @This object. As long as you cache the Field instances, this has no relevance to performance.
但是我怀疑下面的拦截器实现是否有效(如果我理解正确的话)。
public static class DynamicFieldInterceptor {
private final String fieldName;
public DynamicFieldInterceptor(String fieldName) {
this.fieldName = fieldName;
}
public void intercept(@This Object thiz) throws NoSuchFieldException, IllegalAccessException {
Field field = thiz.getClass().getDeclaredField(fieldName);
boolean oldAccessible = field.isAccessible();
field.setAccessible(true);
Long fieldValue = (Long)field.get(thiz);
field.set(thiz, fieldValue + 1L); // !< Instead of my business logic
field.setAccessible(oldAccessible);
}
}
我还尝试了以下想法:为每个字段生成拦截器 classes,并在 @FieldProxy 参数上使用不同的注释。比使用生成的 class 作为目标 class.
的拦截器public interface Metaclass {
void intercept(GetterAndSetter field);
}
public static class MetaclassInterceptor implements Metaclass{
@Override
public void intercept(GetterAndSetter field) {
field.set((Long)field.get() + 1L);
}
}
public static Class<?> annotateInterceptorClass(final String annotation)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return new ByteBuddy()
.subclass(MetaclassInterceptor.class)
.topLevelType()
.name("ClassForIntercepting_" + annotation + "_Field")
.modifiers(Visibility.PUBLIC, Ownership.STATIC)
.defineMethod("intercept", void.class, Visibility.PUBLIC)
.withParameter(GetterAndSetter.class, "intercept")
.annotateParameter(AnnotationDescription.Builder.ofType(FieldProxy.class)
.define("value", annotation).build())
.intercept(SuperMethodCall.INSTANCE)
.make()
.load(MetaclassInterceptor.class.getClassLoader())
.getLoaded();
}
class好像生成的不错。生成的class中的方法存在,并且参数被注解了预期的注解。
然而,当我尝试使用生成的 class 作为拦截器时,出现异常。
Class<?> klass = new ByteBuddy()
.subclass(Object.class)
.defineProperty("index0", Long.class, false)
.defineProperty("index1", Long.class, false)
.defineMethod("doSomeActions", void.class, Visibility.PUBLIC)
.intercept(
MethodDelegation
.withDefaultConfiguration()
.withBinders(FieldProxy.Binder.install(GetterAndSetter.class))
// Use dynamically generated interceptor, see abode
.to(annotateInterceptor("index0"))
.andThen(
MethodDelegation
.withDefaultConfiguration()
.withBinders(FieldProxy.Binder.install(GetterAndSetter.class))
// Use dynamically generated interceptor, see abode
.to(annotateInterceptor("index1"))
)
)
.make()
.load(MetaclassInterceptor.class.getClassLoader())
.getLoaded();
Exception in thread "main" java.lang.NoClassDefFoundError: LClassForIntercepting_index0_Field;
at java.base/java.lang.Class.getDeclaredFields0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3062)
at java.base/java.lang.Class.getDeclaredField(Class.java:2410)
at net.bytebuddy.implementation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:120)
at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:187)
at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:102)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:5662)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:5651)
at MainClass4.main(MainClass4.java:107)
即使我成功地动态实现了拦截器,我也确信这不是完美的方法。我认为必须有可能以更简单的方式进行。真的,@FieldProxy 注解可以从显式指定的名称和bean 中获取字段属性 如果注解中的字段名称没有指定,所以我认为这是一个技术机会,可以将它映射到任何其他字段。
当您使用 load(MetaclassInterceptor.class.getClassLoader())
加载 class 时,您正在创建一个新的 class 加载器,它不会对其他加载器上的任何其他 class 可见,除非你重复使用它。
您可以:
a) 将make
步骤创建的两个DynamicType
组合在一起加载。这样,它们将最终进入相同的 class 加载器。
b) 获取第一个生成的 class 的 class 加载程序并将其转换为 InjectionClassLoader
。您还需要指定 ClassLoadingStrategy.WRAPPER.opened()
并将其与 InjectionClassLoader.Strategy.INSTANCE
一起使用。请注意,这将允许任何引用您生成的 class 实例的人在同一包中定义 classes。
c) 使用 ClassLoadingStrategy.Default.INJECTION
在原始 class 加载程序中定义 classes 的内容,而不创建包装器。并不是说这个策略依赖于 Unsafe API.