Java 是否将代理附加到现有对象?

Java 是否将代理附加到现有对象?,java,proxy,javassist,bytecode-manipulation,Java,Proxy,Javassist,Bytecode Manipulation,我的计划是编写一个基于注释的缓存框架,用于缓存方法的返回值。当使用特定参数第一次调用方法时,缓存应该存储方法返回值。 当使用相同的参数第二次调用相同的方法时,该方法应该从缓存返回先前计算的结果,并且不再执行其代码。 我的注释如下所示: @Cached(cacheProvider = HashMapCacheProvider.class) public Product getProduct(String productId){ // Scraping the product fro

我的计划是编写一个基于注释的缓存框架,用于缓存方法的返回值。当使用特定参数第一次调用方法时,缓存应该存储方法返回值。 当使用相同的参数第二次调用相同的方法时,该方法应该从缓存返回先前计算的结果,并且不再执行其代码。 我的注释如下所示:

@Cached(cacheProvider = HashMapCacheProvider.class)
public Product getProduct(String productId){    
    // Scraping the product from a website ...
    return product;
}
public class Hello {
    public void say() {
         System.out.println("Hello");
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("Hello");
        CtMethod m = cc.getDeclaredMethod("say");
        m.insertBefore("{ System.out.println(\"Hello.say():\"); }");
        Class c = cc.toClass();
        Hello h = (Hello)c.newInstance();
        h.say();
    }
}
目前,我的小框架已经运行良好。我正在使用Javassist创建包含注释方法的类的代理对象。 为了创建新的缓存对象,我使用以下代码:

public static <T> T newCachedInstance(Class<T> clazz)
    throws InstantiationException, IllegalAccessException {

    ProxyFactory factory = new ProxyFactory();
    factory.setSuperclass(clazz);
    factory.setFilter(new MethodFilter() {
        public boolean isHandled(Method m) {
            // ignore finalize()
            return !m.getName().equals("finalize");
        }
    });

    Class<T> c = factory.createClass();

    T proxy = c.newInstance();
    ((ProxyObject) proxy).setHandler(new CachedMethodHandler());
    return proxy;
}

你还有别的想法吗?操作现有对象的方法的最佳实践是什么?

在默认Java虚拟机中,每个对象实例都存储在堆上,其字段数据与对其
类的引用一起存储在堆上(以及用于垃圾收集的小区域)。您基本上是在询问是否可以重新定义此链接以指向另一个默认情况下不可能的

但是,您可以假设使用
sun.misc.Unsafe
用子类覆盖此引用,只要该子类不引入新字段。但是,这一结果还没有定义,我不建议尝试,因为您的框架的用户可能会遇到非常微妙的bug。此外,
sun
包层次结构不适用于可能破坏兼容性的公共用途

附加API将提供另一种方式。您可以使用Java代理在运行时重新定义类。然而,这会影响一个类的所有实例,但从您的目的来看,这是有意义的

另一种可能是在运行前使用AspectJ之类的工具重新定义类

否则,您必须返回一个新实例作为缓存的代理,因为您显然已经在这样做了。这是绝对正确的,并且是Hibernate等主要框架使用的appproach。请注意,javassist比例如cglib要慢得多,因为它直接读取类文件,而不是使用反射访问来避免类加载。这可能会在使用缓存时破坏性能。

声称这样做。不幸的是,它记录了一个使其线程不安全的设计缺陷

说:

该实现有一些流(*),例如,后期的方法代理实例化实际上没有优势,但在代理的多线程执行的情况下,同时可能会受到损害

(*)缺陷


此外,编辑,djcproxy速度较慢。

如果可以替换对要进行代理的对象的所有引用,尤其是如果该对象已存在但只有一个引用, 您可以执行以下操作:

  • 获取对象类并使用Javassist创建代理类
  • 创建此类的实例(假定存在无参数构造函数)
  • 将所有字段复制到该新实例
  • 用对新对象的引用替换原始引用

  • 如果存在副本构造函数,则可以使用它创建现有对象的代理副本。(您必须像
    X$Proxy(X){super(X);}
    那样定义它)

    您可以使用ASM、BCEL或CGLIB。@JoshM但即使是这些也不会修改现有实例,这似乎是OP想要做的。我会使用一个现有的缓存框架,忘记这个愚蠢的地方。@Kayaman你知道有哪个缓存框架能够缓存依赖于方法参数的方法结果吗?字节码操作并不像你想象的那么愚蠢。许多流行的框架,如Hibernate、Struts和Spring都在使用它。如果这是愚蠢的,那么所有的AOP和声明式编程都是愚蠢的。我认为这实际上是一个好主意。是不是没有提供您试图实现的功能?