I want to wrap simple POJO class. The thing is I know nothing about that class beforehand, only that it's POJO with setters and getters. I want to substitute this class with my Proxyclass so that every time client calls getter or setter I would be able to intercept that call. So when the call is intercepted, I want to do some pre-get(or set) operation, then invoke the getter(or setter), and then to do some post-get(or set) operations. I'm creating my proxy like that
private Pojo generatePojoProxy(Class<? extends PojoInterface> myPojo) {
Class<?> PojoProxyClass;
PojoProxyClass = new ByteBuddy()
.subclass(myPojo)
.method(ElementMatchers.nameStartsWith("get"))
.intercept(MethodDelegation.to(GetterInterceptor.class))
.method(ElementMatchers.nameStartsWith("set"))
.intercept(MethodDelegation.to(SetterInterceptor.class))
.name("Proxy" + myPojo.getName())
.make()
.load(myPojo.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
Object pojoProxyInstance = null;
try {
pojoProxyInstance = PojoProxyClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return (Pojo) pojoProxyInstance;
}
My GetterInterceptor looks like that
public class GetterInterceptor {
@RuntimeType
public static Object intercept(@AllArguments Object[] allArguments, @Origin Method method, @Super(proxyType = TargetType.class) Object delegate) {
preGetHandle();
Object result = null;
try {
result = method.invoke(delegate, allArguments);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
postGetHandle();
return result;
}
private static void preGetHandle() {}
private static void postGetHandle() {}
And setter looks the same.
But when I set and get something from my Proxyclass instance, it goes much slower (1.5-2 times slower), than with initial Pojo class instance. Am I doing something wrong? I believe, there must be some way to make it faster.
Any help is appreciated!
I measure the performance the following way
public class Main {
private static final int LOOP_COUNT = 10_000_000;
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Pojo pojo = new Pojo();
Pojo myProxy = (Pojo) ProxyFactory.getInstance().getProxy(Pojo.class);
testTime(pojo);
testTime(myProxy);
}
private static void testTime(Pojo pojo) {
long startTime = System.currentTimeMillis();
Random random = new Random();
long totalSum = 0;
for (int i = 0; i<LOOP_COUNT; i++){
pojo.setId(random.nextLong());
totalSum += pojo.getId();
}
long endTime = System.currentTimeMillis();
System.out.println(pojo.getClass() + " time = " + (endTime-startTime) + " total= " + totalSum);
}
My results for that are
class Pojo time = 288 total= 1060564671495946244
class ProxyPojo time = 738 total= 5879857558672375335
This is only a partial answer I'm afraid... Reflection is what's slowing you down so to skip reflection your solution needs to be of the form:
Alas I don't know what the bytebuddy voodoo is exactly. It's all in there somewhere.
Most of this slowdown is simply the cost of doing business via reflection.
Consider this test:
I get the following results:
As you can see, using reflection is about twice as slow. This shouldn't be a huge surprise, those try blocks and method.invoke calls mean you're doing a whole bunch of extra work that the base pojo doesn't need to do.
As pointed out, you should avoid the reflective invocation. In Byte Buddy, use the
@SuperCall
injection:For a setter, you do not need to return a value so you can use a runnable.