65.9K
CodeProject 正在变化。 阅读更多。
Home

使用字节码操作的替代代理类

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (1投票)

2012年5月20日

CPOL

1分钟阅读

viewsIcon

11112

downloadIcon

53

本文提供了使用字节码操作的 Java Proxy 类的替代方案。

介绍 

代理强制对象方法调用通过代理对象间接发生,代理对象充当被代理底层对象的替代或委托。通常,代理对象被声明为客户端对象不知道它们拥有代理对象实例。

java.lang.reflect.Proxy 类存在一些缺点

  1. 它大量使用了反射。
  2. 处理方法的代码不够清晰。

invoke 方法有时会变得臃肿。

使用代码

Proxy.newProxyInstance(Class clazz, Map<Class, Object> interfaceMap, Object... args)

clazz - 代理基础类  

interfaceMap - 键是接口类,值是 

  • 或者 实现该接口的对象 
  • 或者实现相应接口且具有公共无参数构造函数的具体类(非抽象类) 
args - 传递给构造函数的参数,用于创建代理类实例(与 clazz 参数相同)

基础类只是一个简单的类(至少有一个非私有构造函数)。如果方法的签名与接口中的一个方法相同,则将调用该函数,而不是实际实现中提供的函数(即 interfaceMap 中的值)。 

您还可以使基础类抽象,实现接口并仅定义选定的方法。

例如,

public /*abstract*/ class Base /*implements Runnable, Callable, Comparable*/  {
 
    //method from Callable
    public Object call() throws Exception {
        System.out.println("Call start");
        Object o = (Proxy.<Callable> getInnerObject(this, Callable.class))
                .call();
        System.out.println("Call end");
        return o;
    }
 
    //method from Comparable
    public int compareTo(Object o) {
        System.out.println("compareTo called with argument - " + o);
        return 0;
    }
}

上面的 compareTo 将被调用,而不是在 interfaceMap 中传递的实现

interfaceMap.put(Comparable.class, new Comparable(){
    @Override
    public int compareTo(Object o) {
        System.out.println("This will not be called as inner object is not accessed from base class.");
        return 0;
    }
});
 
interfaceMap.put(Runnable.class, MyRunnable.class);
interfaceMap.put(Callable.class, MyCallable.class);
Runnable runnable = (Runnable)Proxy.newProxyInstance(Base.class, interfaceMap);
Thread thread = new Thread(runnable);
thread.start(); 

您可以使用类似以下方式访问实际实现对象(即 interfaceMap 中的值):

Callable callable = Proxy.<Callable> getInnerObject(this, Callable.class); 

其中 this 是基础类的实例,而 Callable.class 是传递到 interfaceMap 中的键。 

如果您希望没有人能够直接访问您的内部对象(即通过使用 Proxy.<Callable> getInnerObject),您可以使用 newProxyInstance 的重载方法,该方法限制从基础类以外的任何其他类调用此函数。  

字节码操作仅在第一次调用 newProxyInstance 时使用。 

© . All rights reserved.