ByteBuddy перехватывает сеттер и вызывает метод с аргументами, зависящими от объекта, для которого вызывается метод сеттера - PullRequest
2 голосов
/ 15 апреля 2020

Я хочу перехватить каждый метод установки, вызванный во всех экземплярах одного класса, а затем вызвать метод для другого объекта класса с аргументами, соответствующими значениям некоторых полей каждого экземпляра первого класса. Для этой цели я хочу использовать API ByteBuddy, но я также хочу создать только один подкласс для всех экземпляров указанного класса

Например, я написал следующий код:

public final class AttributesIntercepted<T> {

    private static EnumMap<ResourceType, Class> attributesClasses = new EnumMap<>(ResourceType.class);

    private Target target;
    private T attributes;
    private Resource resource;

    private AttributesIntercepted(T attributes, Target target, Resource resource) {
        this.attributes = attributes;
        this.target = target;
        this.resource = resource;
    }

    private T makeInstance() {
        T instance = null;
        try {
            Class subClass;
            if (!attributesClasses.containsKey(resource.getType())) {
                subClass = new ByteBuddy()
                        .subclass(attributes.getClass())
                        .method(ElementMatchers.isSetter())
                        .intercept(SuperMethodCall.INSTANCE.andThen(
                                MethodCall.invoke(target.getClass().getMethod("updateResource", Resource.class))
                                        .on(target)
                                        .with(resource)
                        )).make()
                        .load(attributes.getClass().getClassLoader())
                        .getLoaded();
                attributesClasses.put(resource.getType(), subClass);
            } else {
                subClass = attributesClasses.get(resource.getType());
            }

            // create the new instance of this subclass
            instance = (T) (subClass.getDeclaredConstructor(attributes.getClass()).newInstance(attributes));
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
System.out.println(e.toString());
        }

        return instance;
    }

    public static<T> T create(T attributes, Target target, Resource resource) {
        return (T) (new AttributesIntercepted(attributes, target, resource).makeInstance());
    }
}

Я сохраняю каждый подкласс, созданный для каждого типа ресурса на карте, чтобы создать только один подкласс для каждого типа ресурса. Проблема здесь состоит в том, что для всех экземпляров созданного подкласса значение аргумента 'resource', передаваемого методу updateResource, вызываемому для целевого объекта, всегда будет одинаковым. Кажется, что аргумент, переданный целевому объекту, оценивается при создании подкласса, а не при вызове сеттеров

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

Спасибо за вашу помощь

1 Ответ

1 голос
/ 16 апреля 2020

Вам следует использовать TypeCache от Byte Buddy и избегать своего собственного решения, это поможет вам избежать некоторых распространенных проблем.

Что касается перехвата: определите поля для этих значений, используя defineField в вашем DSL. Тогда вместо on(target) вы делаете onField(...) и withField(...). После создания класса вам теперь нужно установить эти поля для любого конкретного экземпляра, который вы создаете, таким образом, вы можете повторно использовать класс на все случаи жизни.

...