Установка поля @FieldProxy динамически в Byte Buddy - PullRequest
1 голос
/ 11 мая 2019

Мне нужно реализовать перехватчик, который можно использовать для динамически указанных полей независимо от имени поля.

На комментарий к ответу здесь 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-компонента, если имя поля в аннотации не указано, поэтому я думаю, что это техническая возможность сопоставить его с любым другим полем.

1 Ответ

0 голосов
/ 11 мая 2019

Когда вы загружаете класс с помощью load(MetaclassInterceptor.class.getClassLoader()), вы создаете новый загрузчик классов, который не становится видимым для других классов на других загрузчиках, если вы не используете его повторно.

Вы можете:

a) Объедините два DynamicType s, которые созданы с помощью шага make, и загрузите их вместе. Таким образом, они окажутся в одном загрузчике классов.

b) Возьмите загрузчик классов первого сгенерированного класса и приведите его к InjectionClassLoader. Вам также нужно будет указать ClassLoadingStrategy.WRAPPER.opened() и использовать его вместе с InjectionClassLoader.Strategy.INSTANCE. Обратите внимание, что это позволит любому, имеющему ссылку на экземпляр вашего сгенерированного класса, определять классы в том же пакете.

c) Используйте ClassLoadingStrategy.Default.INJECTION, который определяет классы в исходном загрузчике классов без создания оболочки. Не то чтобы эта стратегия опиралась на небезопасный API.

...