Reputation: 21
I want to intercept every setter method called on all instances of one class, and then call a method on another class object, with arguments corresponding to some fields values of each instance of the first class. For that purpose, I want to use the ByteBuddy API, but I also want to create only one subclass for all instances of a specified class
For example, I wrote the following code :
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());
}
}
I save each subclass created for each resource type in a map, in order to create only one subclass for each resource type. The problem here, is that for all instances of the subclass created, the value of the argument 'resource' passed to the method 'updateResource' called on the target object will always be the same. It seems that the argument passed to the target object is evaluated when creating the subclass and not when calling the setters
If I just put in comments the code that save the subclasses in the map, it works, but, as i said, i only want to create one subclass for each resource type ....
Thanks for your help
Upvotes: 2
Views: 647
Reputation: 44067
You should use Byte Buddy's TypeCache
and avoid your own solution, it will help you avoid some common problems.
As for your interception: define fields for these values using defineField
in your DSL. Then instead of on(target)
, you do onField(...)
and withField(...)
. After creating the class you now need to set those fields for any particular instance you create, this way you can reuse the class for all occasions.
Upvotes: 1