在上一篇文章设计模式之代理模式 https://www.codeliu.com/设计模式/420.html 里, 我们说了 JDK 动态代理类, 不过我相信好多人都有疑惑, 包括我, 刚开始学的时候都是一脸懵逼, 那么到底疑惑在哪里呢?
我的疑惑就是这个 InvocationHandler 的 invoke 方法到底是有啥用? 我们都没有调用它. newProxyInstance 返回的东西到底是啥? 等等, 这篇文章我们就一起来探讨一下吧.
首先我们先写一个简单的动态代理吧, 有例子更好说明. 还是买车的例子, 哈哈, 说明我挺想买车的. 我就直接复制上篇文章的.
- // Car.java
- package com.codeliu.dao;
- public interface Car {
- // vip 客户可以直接买车
- public void buyCar();}
- // CarImp1.java
- package com.codeliu.dao.impl;
- import com.codeliu.dao.Car;
- /**
- * 委托类 1
- */
- public class CarImp1 implements Car {
- public void buyCar() {
- System.out.println("我是 vip1");
- }
- }
- // CarProxy.java
- package com.codeliu.dao.impl;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- /**
- * 动态代理类
- */
- public class CarProxy implements InvocationHandler {
- private Object target;
- /**
- * 绑定一个委托对象并获得一个代理类对象
- * @param target [description]
- * @return [description]
- */
- public Object bind(Object target) {
- this.target = target;
- // 取得代理对象
- return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
- this);
- }
- @Override
- // 这个方法并不是我们自己去调用
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- System.out.println("调用这个方法前:" + method);
- // 执行委托类的方法
- Object result = method.invoke(target,args);
- System.out.println("调用这个方法后:" + method);
- return result;
- }
- }
- package com.codeliu.test;
- import com.codeliu.dao.impl.CarImp1;
- import com.codeliu.dao.impl.CarImp2;
- import com.codeliu.dao.impl.CarProxy;
- import com.codeliu.dao.Car;
- public class TestProxy {
- public static void main(String[] args) {
- CarProxy cp = new CarProxy();
- // 传入一个实现了该接口的实例就行
- Car car = (Car)cp.bind(new CarImp1());
- // Car car = (Car)cp.bind(new CarImp2());
- car.buyCar();
- }
- }
好了, 那么现在我们就来看看 Test.java 里生成的 car 对象到底是个什么东 a 西, 是我们的 Car 接口吗?(我一开始一直理解不通)
我们利用反射可以获取到 car 对应类的大部分信息, 好了, 现在我们把 Test.java 改成如下形式
- package com.codeliu.test;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import com.codeliu.dao.Car;
- import com.codeliu.dao.impl.CarImp1;
- import com.codeliu.dao.impl.CarProxy;
- public class Test {
- public static void main(String[] args) {
- CarProxy cp = new CarProxy();
- // 传入一个实现了该接口的实例就行
- Car car = (Car)cp.bind(new CarImp1());
- // Car car = (Car)cp.bind(new CarImp2());
- car.buyCar();
- System.out.println("car 是 Proxy 类的子类吗?" + (car instanceof Proxy));
- Class<?> c = car.getClass();
- System.out.println(c);
- System.out.println("car 的 Class 类是:" + c.getName());
- Field[] fields = c.getDeclaredFields();
- System.out.print("car 的 Class 类中有哪些字段:");
- for(Field f:fields) {
- System.out.print(f.getName() + " ");
- }
- System.out.println();
- System.out.print("car 的 Class 类中有哪些方法:");
- Method[] methods = c.getDeclaredMethods();
- for(Method m:methods) {
- System.out.print(m.getName() + " ");
- }
- System.out.println();
- System.out.println("car 的 Class 类的父类是:" + c.getSuperclass());
- Class<?>[] class1 = c.getInterfaces();
- System.out.print("car 的 Class 类实现的接口有:");
- for(Class<?> c1:class1) {
- System.out.println(c1.getName() + " ");
- }
- }
- }
运行一下, 输出如下
调用这个方法前: public abstract void com.codeliu.dao.Car.buyCar()
我是 vip1
调用这个方法后: public abstract void com.codeliu.dao.Car.buyCar()
car 是 Proxy 类的子类吗? true
class com.sun.proxy.$Proxy0
car 的 Class 类是: com.sun.proxy.$Proxy0
car 的 Class 类中有哪些字段: m1 m2 m3 m0
car 的 Class 类中有哪些方法: equals toString hashCode buyCar
car 的 Class 类的父类是: class java.lang.reflect.Proxy
car 的 Class 类实现的接口有: com.codeliu.dao.Car
恩, 发现了什么, 这个玩意竟然是一个 Proxy 类的子类, 它的名字是 $Proxy0, 实现了我们自己定义的 Car 接口, 还有四个字段和方法.
既然知道了上面的结果, 那么我们就去看看 newProxyInstance 方法的源码, 应该能找到答案了.
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h)
- throws IllegalArgumentException
- {
- Objects.requireNonNull(h);
- /*
- * Look up or generate the designated proxy class.
- */
- Class<?> cl = getProxyClass0(loader, intfs);
- }
我就截了比较重要的, getProxyClass0 方法, 它的作用就是生成一个 proxy 类, 好, 我们看看这个方法.
- private static Class<?> getProxyClass0(ClassLoader loader,
- Class<?>... interfaces) {
- if (interfaces.length> 65535) {
- throw new IllegalArgumentException("interface limit exceeded");
- }
- // If the proxy class defined by the given loader implementing
- // the given interfaces exists, this will simply return the cached copy;
- // otherwise, it will create the proxy class via the ProxyClassFactory
- return proxyClassCache.get(loader, interfaces);
- }
再去看看这个 get 方法, emmmmm, 代码太多了, 尼玛, 我也看不太懂, 不过经过我查资料, 总结如下
newProxyInstance 方法调用 getProxyClass0 生成一个 $Proxy0 类.
恩没了, 现在我们要找答案就得去看看 $Proxy0 类的源码了.
因为生成的是一个. class 文件, 所以我们先反编译后, 可以看到源码如下
- package com.sun.proxy;
- import com.codeliu.dao.Car;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.lang.reflect.UndeclaredThrowableException;
- public final class $Proxy0
- extends Proxy
- implements Car
- {
- private static Method m1;
- private static Method m2;
- private static Method m3;
- private static Method m0;
- public $Proxy0(InvocationHandler paramInvocationHandler)
- {
- super(paramInvocationHandler);
- }
- public final boolean equals(Object paramObject)
- {
- try
- {
- return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- public final String toString()
- {
- try
- {
- return (String)this.h.invoke(this, m2, null);
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- public final void buyCar()
- {
- try
- {
- this.h.invoke(this, m3, null);
- return;
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- public final int hashCode()
- {
- try
- {
- return ((Integer)this.h.invoke(this, m0, null)).intValue();
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- static
- {
- try
- {
- m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
- m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
- m3 = Class.forName("com.codeliu.dao.Car").getMethod("buyCar", new Class[0]);
- m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
- return;
- }
- catch (NoSuchMethodException localNoSuchMethodException)
- {
- throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
- }
- catch (ClassNotFoundException localClassNotFoundException)
- {
- throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
- }
- }
- }
$Proxy0 继承了 Proxy 类并实现了 Car 接口, 它有一个构造方法, 传入了一个 InvocationHandler 实例并调用了父类的构造方法, 然后是四个 final 方法, 其他三个是 Object 类传过来的, 还有一个叫 buyCar, 这不就是我们在接口中定义的吗? 我们看看它怎么写的
- public final void buyCar()
- {
- try
- {
- this.h.invoke(this, m3, null);
- return;
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
恩, 首先 m3 在静态块中, 取得 Car 接口中的 buyCar 方法, 然后是调用 invoke 方法, 666, 现在终于解决了文章开头的疑惑, 原来在这里调用了, 等等, 那么这个 h 又是什么东西, 我们看看 Proxy 的源码
- protected InvocationHandler h;
- protected Proxy(InvocationHandler h) {
- Objects.requireNonNull(h);
- this.h = h;
- }
一个字段和一个带参的构造函数, 我们再看看 $Proxy0 的源码中有这么一段
- public $Proxy0(InvocationHandler paramInvocationHandler)
- {
- super(paramInvocationHandler);
- }
好家伙, 到这里就真相大白了, 虽然我还是不知道这个 $Proxy0.class 怎么生出来的, 但基本也懂了, 再总结一下整趟流程.
首先是我们写了一个接口, 一个委托类实现了这个接口, 然后写一个代理类实现 InvocationHandler 接口并重写 invoke 方法, 之后调用 Proxy 的 newProxyInstance 方法, 这个方法调用 getProxyClass0 方法生成一个动态代理对象, 这个对象对应的类是 $Proxy0,$Proxy0 继承了 Proxy 类并且实现了我们自己写的接口, 在这个类中, 有静态字段和 final 修饰的方法, 其中有我们在接口中定义的方法 (为了好表述, 我先把它叫做 method), 通过 method 方法, 调用我们在代理类中重写的 invoke, 而我们在 invoke 方法中, 又调用了委托类的方法, 所以表面上好像是直接调用了委托类的方法, 其实绕了一大圈, 是系统帮我们调用了.
不知道大家有没有看懂, 没有看懂就多看几遍, 因为我说的也有点蒙圈了. 今天连续写了两篇文章, 好饿啊, 不过弄懂了困扰我的问题, 贼开心.
我是一个追求完美的人啊, 我好想画一个流程图啊, 不然一大段文字, 不形象又难懂, 不过我没力气了, 有没有好心人画一个啊.==
来源: http://www.jianshu.com/p/bbe3bd0f718d