动态代理详解

代理 是常用的基本设计模式之一,在某些情况下,一个客户不想或者不能直接引用一个对 象,此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。
通过引入一个新的对象(如小图片和远程代理 对象)来实现对真实对象的操作或者将新的对象作为真 实对象的一个替身,这种实现机制即为 代理模式

简单代理

**具体实现:**给某一个对象提供一个代 理,并由代理对象控制对原对象的引用。
下面来看一个代理结构的简单示例:

import java.io.Serializable;

interface Interface {
  void doSomething();
  void somethingElse(String arg);
}
//被代理类
class RealObject implements Interface, Serializable {
  public void doSomething() { System.out.println("doSomething"); }
  public void somethingElse(String arg) {
    System.out.println("somethingElse " + arg);
  }
}    
//代理类
class SimpleProxy implements Interface {
  private Interface proxied;
  public SimpleProxy(Interface proxied) {
    this.proxied = proxied;
  }
  public void doSomething() {
    System.out.println("SimpleProxy doSomething");
    proxied.doSomething();
  }
  public void somethingElse(String arg) {
    System.out.println("SimpleProxy somethingElse " + arg);
    proxied.somethingElse(arg);
  }
}    

class SimpleProxyDemo {
  public static void consumer(Interface iface) {
    iface.doSomething();
    iface.somethingElse("bonobo");
  }
  public static void main(String[] args) {
    consumer(new RealObject());
    consumer(new SimpleProxy(new RealObject()));
  }
} 

Output:
doSomething
somethingElse bonobo
SimpleProxy doSomething
doSomething
SimpleProxy somethingElse bonobo
somethingElse bonobo

因为consumer()接受的是Interface,所以它无法知道正在获得的到底是RealObject还是SimpleProxy,因为这两者都实现了Interface。但是SimpleProxy已经被插入到了客户端和RealObject之间,因此它会执行操作,然后调用RealObject上相同的方法。

在任何时刻,只要你想要将额外的操作从"实际"对象中分离到不同的地方,特别是当你希望能够很容易的做出修改,从没有使用额外操作转为使用这些操作时,代理就会显得非常有用。例如,如果你希望跟踪对RealObject中的方法的调用,或者希望度量这些调用的开销,那么你应该怎么做呢?这时,使用代理就可以很容易地添加或移除它们。

动态代理

Java 的 动态代理 比代理的思想更向前迈进的一步,因为它可以动态的创建代理并动态的处理对所代理方法的调用。下面是从修改后的SimpleProxyDemo.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Interface {
  void doSomething();
  void somethingElse(String arg);
}

class RealObject implements Interface {
  public void doSomething() { System.out.println("doSomething"); }
  public void somethingElse(String arg) {
      System.out.println("somethingElse " + arg);
  }
}
class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    public DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.print("**** proxy: " + proxy.getClass() + ", method: "
                + method + ", args: " );
        if (args != null)
            for (Object arg : args)
                System.out.print(" " + arg);
        else
            System.out.print("null");
        System.out.println();
        return method.invoke(proxied, args);
    }
}

class SimpleDynamicProxy {
    public static void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("bonobo");
    }

    public static void main(String[] args) {
        RealObject real = new RealObject();
        // consumer(real);
        // Insert a proxy and call again:
        Interface proxy = (Interface) Proxy.newProxyInstance(
                Interface.class.getClassLoader(),
                new Class[] { Interface.class }, new DynamicProxyHandler(real));
        consumer(proxy);
    }
}

Output:
**** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null
doSomething
**** proxy: class Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: bonobo
somethingElse bonobo

通过调用静态方法Proxy.newProxyInstance()可以创建动态的创建代理类对象,这个方法需要得到一个类加载器(你通常可以从已经被加载的对象中获取其类加载器,然后传递给它),一个你希望实现的接口列表(不是类或抽象类),以及**InvocationHandler接口的一个实现。
动态代理可以将所有的调用重定向到
调用处理器**上,因此会向调用处理器的构造器传递一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可以请求转发。
mark

动态代理示意图

从上图我们可以看到,代理类Proxy调用的所有方法都会转发到实现了InvocationHandler接口的invoke方法。Proxy不管客户端的业务方法是怎么实现的。当客户端调用Proxy时,它只会调用InvocationHandlerinvoke接口,所以我们的真正实现的方法就必须在invoke方法中去调用。

调用处理器中实现的invoke()方法中传递进来了代理对象proxy,可以通过此参数来区分请求的来源。method既是实际要调用的被代理类的方法,args为被调用方法所需要的参数。

动态代理之代理对象

那么proxy到底是一个什么样的对象呢,这个类到底是长什么样子呢?好的。我们再写二个方法去把这个类打印出来看个究竟,是什么三头六臂呢?我们在main下面写如下两个静态方法。

	public static void printClassDefinition(Class clz) {

		StringBuilder clzModifier = new StringBuilder();
		int mod = clz.getModifiers() & Modifier.methodModifiers();
		if (mod != 0) {
			clzModifier.append(Modifier.toString(mod)).append(' ');
		}
		String superClz = clz.getSuperclass().getName();
		if (superClz != null && !superClz.equals("")) {
			superClz = "extends " + superClz;
		}

		Class[] interfaces = clz.getInterfaces();

		String inters = "";
		for (int i = 0; i < interfaces.length; i++) {
			if (i == 0) {
				inters += "implements ";
			}
			inters += interfaces[i].getName();
		}

		System.out.println(clzModifier + clz.getName() + " " + superClz + " "
				+ inters);
		System.out.println("{");

		Field[] fields = clz.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			System.out.println("\t" + fields[i].toString() + ';');
		}

		System.out.println();
		Constructor[] constructors = clz.getDeclaredConstructors();
		for (int i = 0; i < constructors.length; i++) {
			System.out.println("\t" + constructors[i].toString() + ';');
		}
		System.out.println();
		Method[] methods = clz.getDeclaredMethods();
		for (int i = 0; i < methods.length; i++) {
			System.out.println("\t" + methods[i].toString() + ';');
		}
		System.out.println("}");
	}

再改写main方法:

    public static void main(String[] args) {
        RealObject real = new RealObject();
        // consumer(real);
        // Insert a proxy and call again:
        Interface proxy = (Interface) Proxy.newProxyInstance(
                Interface.class.getClassLoader(),
                new Class[] { Interface.class }, new DynamicProxyHandler(real));
        System.out.println(proxy.getClass().getName());
        //打印出proxy类的结构
        printClassDefinition(proxy.getClass());
    }

现在重新执行main方法,我们再看看输出结果:

Output:
$Proxy0
public final $Proxy0 extends java.lang.reflect.Proxy implements Interface
{
	private static java.lang.reflect.Method $Proxy0.m1;
	private static java.lang.reflect.Method $Proxy0.m0;
	private static java.lang.reflect.Method $Proxy0.m3;
	private static java.lang.reflect.Method $Proxy0.m4;
	private static java.lang.reflect.Method $Proxy0.m2;

	public $Proxy0(java.lang.reflect.InvocationHandler);

	public final boolean $Proxy0.equals(java.lang.Object);
	public final java.lang.String $Proxy0.toString();
	public final int $Proxy0.hashCode();
	public final void $Proxy0.doSomething();
	public final void $Proxy0.somethingElse(java.lang.String);
}

此时,我们就可以看到Proxy对象的类结构,那么很明显,Proxy.newProxyInstance(Interface.class.getClassLoader(),new Class[] { Interface.class }, new DynamicProxyHandler(real))方法会做如下几件事:

  1. 根据传入的第二个参数 interfaces 动态生成一个类,实现 interfaces 中的接口,该例中即 Interface 接口的 somethingElsedoSomething 方法。并且继承了 Proxy 类,重写了 hashcode,toString,equals 等三个方法。具体实现可参看 ProxyGenerator.generateProxyClass(...); 该例中生成了$Proxy0类。
  2. 通过传入的第一个参数classloder将刚生成的类加载到jvm中。即将$Proxy0类load。
  3. 利用第三个参数,调用$Proxy0$Proxy0(InvocationHandler)构造函数 创建$Proxy0的对象,并且用interfaces参数遍历其所有接口的方法,并生成Method对象初始化对象的几个Method成员变量
  4. $Proxy0的实例返回给客户端。

现在好了。我们再看客户端怎么调就清楚了。

  1. 客户端拿到的是$Proxy0的实例对象,由于$Proxy0实现了Interface,因此转化为Interface没任何问题。
    Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[] { Interface.class }, new DynamicProxyHandler(real));
  2. proxy.doSomething()
    实际上调用的是$Proxy0.doSomething(), 那么$Proxy0.doSomething()的实现就是通过InvocationHandler去调用invoke方法啦!

参考文章:
深入理解Java Proxy机制

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值