Мне нужно реализовать перехватчик, который можно использовать для динамически указанных полей независимо от имени поля.
На комментарий к ответу здесь
https://stackoverflow.com/a/35113359/11390192
Я прочитал
Вы можете просто использовать отражение на объекте @This. До тех пор, как вы
кэшируйте экземпляры Field, это не имеет отношения к производительности.
Однако я сомневаюсь, что следующая реализация перехватчика является эффективной (если я правильно понял комментарий).
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);
}
}
Я также попробовал следующую идею: создать классы перехватчиков для каждого поля с различными аннотациями в аргументе @FieldProxy. Чем использовать сгенерированный класс в качестве перехватчика для целевого класса.
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<?> 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-компонента, если имя поля в аннотации не указано, поэтому я думаю, что это техническая возможность сопоставить его с любым другим полем.