java - Bytebuddy如何高效包装POJO?

标签 java proxy-classes byte-buddy

我想包装简单的 POJO 类。问题是我事先对那个类一无所知,只知道它是带有 setter 和 getter 的 POJO。我想用我的 Proxyclass 替换此类,以便每次客户端调用 getter 或 setter 时我都能够拦截该调用。所以当调用被拦截时,我想做一些pre-get(or set)操作,然后调用getter(or setter),然后做一些post-get(or set)操作。 我正在这样创建我的代理

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

我的 GetterInterceptor 看起来像那样

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

和 setter 看起来一样。

但是当我设置并从我的 Proxyclass 实例中获取某些东西时,它比初始 Pojo 类实例慢得多(慢 1.5-2 倍)。 难道我做错了什么?我相信,一定有办法让它更快。

感谢任何帮助!

我用以下方式衡量性能

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

我的结果是

class Pojo time = 288 total= 1060564671495946244
class ProxyPojo time = 738 total= 5879857558672375335

最佳答案

如前所述,您应该避免反射调用。在 Byte Buddy 中,使用 @SuperCall 注入(inject):

public class GetterInterceptor {

  @RuntimeType
  public static Object intercept(@SuperCall Callable<?> zuper) throws Exception {
    preGetHandle();
    try {
       return zuper.call();
    } finally {
      postGetHandle();
    }
  }

  private static void preGetHandle() {}
  private static void postGetHandle() {}
}

对于setter,你不需要返回一个值,所以你可以使用runnable。

关于java - Bytebuddy如何高效包装POJO?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37719089/

相关文章:

c# - 从没有代理类的数据库加载?

c++ - 这种设计模式的名称是什么?门面、适配器、网桥、代理?

用于测试和生产 Web 服务的 c# 代理类

java - 使用 ByteBuddy 的检测不适用于第 3 方类

java - 创建语句失败

java - 在单独的机器上运行 bazel 远程执行程序测试

java - 为什么 Java 使用堆来分配内存?

java - 使用 bytebuddy 定义字段后如何建议原始类的构造函数

java - 如何在 ByteBuddy 代理中检测/拦截 Method.invoke?

java - 在 spring bean 中访问 Db 时的线程安全