首页 > 解决方案 > ByteBuddy 拦截 setter 并使用参数调用方法,具体取决于调用 setter 方法的对象

问题描述

我想拦截在一个类的所有实例上调用的每个 setter 方法,然后在另一个类对象上调用一个方法,其参数对应于第一个类的每个实例的某些字段值。为此,我想使用 ByteBuddy API,但我也想为指定类的所有实例只创建一个子类

例如,我编写了以下代码:

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());
    }
}

我将每个资源类型创建的每个子类保存在一个地图中,以便为每个资源类型只创建一个子类。这里的问题是,对于创建的子类的所有实例,传递给目标对象上调用的方法“updateResource”的参数“resource”的值将始终相同。似乎传递给目标对象的参数是在创建子类时评估的,而不是在调用 setter 时

如果我只是在注释中将保存子类的代码放在地图中,它可以工作,但是,正如我所说,我只想为每种资源类型创建一个子类......

谢谢你的帮助

标签: javasetterbyte-buddyintercept

解决方案


您应该使用 Byte BuddyTypeCache并避免使用自己的解决方案,它会帮助您避免一些常见问题。

至于您的拦截:defineField在您的 DSL 中使用这些值定义字段。然后代替on(target),你做onField(...)withField(...)。创建类后,您现在需要为您创建的任何特定实例设置这些字段,这样您就可以在所有场合重用该类。


推荐阅读