1. 先理一下动态代理实现的思路:
实现功能: 自己定义一个类 Proxy, 通过Proxy的静态方法 newProxyInstance(Class<T> intface,InvocationHandler h)返回代理对象, intface: 被代理类的接口对象, h: InvocationHandler的实例对象
1). 声明一段动态代理类的源码( 动态产生代理类 )
2). 编译动态代理类的源码( JDK Compiler API ), 产生代理类
3). 通过 ClassLoader 加载这个代理类, 创建一个代理类的实例对象
4). return 返回这个代理对象
2. 代码实现:
为什么代理类的类名为 $Proxy0?
这是因为 Java中动态代理, 生成的代理类的类名就是 $Proxy0, 依葫芦画瓢而已,
可以用一个动态代理对象 proxy, 来验证:
System.out.println(proxy.getClass().getName()); //输出com.sun.proxy.$Proxy0
public class Proxy { /** * @param intface * 被代理类的接口的类对象 * @param h * InvocationHandler的实例对象 * @return proxy 生成的动态代理对象 * @throws Exception */ @SuppressWarnings("unchecked") public static <T> T newProxyInstance(Class<T> intface,InvocationHandler h) throws Exception { String srcStr = ""; // 代理类$Proxy0的源码, 字符串形式 String methodStr = ""; // 代理类$Proxy0的所有代理方法, 字符串形式 String rt = "\r\n"; // Windows平台下的换行符 // 动态生成代理类$Proxy0的所有代理方法 for(Method m : intface.getMethods()) { methodStr += " @Override" + rt + " public void " + m.getName() + "() {" + rt + " try {" + rt + " Method method = " + intface.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt + " h.invoke(this, method, null);" + rt + // 暂时只支持无参方法 " } catch (Throwable e) { e.printStackTrace(); }" + rt + " }"; } // 拼接代理类$Proxy0的源码 srcStr += "package proxy.my;" + rt + "import java.lang.reflect.Method;" + rt + "import java.lang.reflect.InvocationHandler;" + rt + "public class $Proxy0 implements " + intface.getName() + "{" + rt + " private InvocationHandler h;" + rt + " public $Proxy0(InvocationHandler h) {" + rt + " this.h = h;" + rt + " }" + rt + " "+methodStr + rt + "}"; // 生成代理类的java源文件 String srcFilePath = System.getProperty("user.dir") + "/bin/proxy/my/$Proxy0.java"; File srcFile = new File(srcFilePath); // 使用commons-io-2.2.jar中的FileUtils, 向一个指定的文件中写入指定的字符串 FileUtils.writeStringToFile(srcFile, srcStr); // 编译这个代理类的java源文件 // 获取编译器 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 获取文件管理者 StandardJavaFileManager fileMgr= compiler.getStandardFileManager(null, null, null); // 获取文件 Iterable<? extends JavaFileObject> compilationUnits = fileMgr.getJavaFileObjects(srcFilePath); // 获取编译任务 CompilationTask task = compiler.getTask(null, fileMgr, null, null, null, compilationUnits); // 编译 task.call(); // 获取代理类的类加载器 ClassLoader classLoader = Proxy.class.getClassLoader(); // 加载代理类 Class<?> clazz = classLoader.loadClass("proxy.my.$Proxy0"); // 获取代理类的构造器 Constructor<?> constructor = clazz.getConstructor(InvocationHandler.class); // 通过代理类的构造器, 创建一个代理类的实例, 也就是代理对象, 返回代理对象 T proxy = (T) constructor.newInstance(h); return proxy; } }
3. 测试自己实现的 Proxy.newProxyInstance() 方法
先定义一个 ProxyInvocationHandler 类, 该类实现了java.lang.reflect.InvocationHandler接口, 实现invoke()方法
public class ProxyInvocationHandler implements InvocationHandler { private Object target; // 被代理的目标对象 public ProxyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("来不及解释了, 快上车"); method.invoke(target, args); // 调用目标对象的方法 System.out.println("下车了, 快记住车牌号"); return null; // 暂时只支持无参方法 } }
测试动态代理
public static void main(String[] args) throws Throwable { Moveable car = new Car(); InvocationHandler h = new ProxyInvocationHandler(car); Moveable carProxy = Proxy.newProxyInstance(Moveable.class, h); carProxy.move(); }
Console输出: // 其实我想当个老司机, 天天飙车
来不及解释了, 快上车
飙车中...
下车了, 快记住车牌号
Moveable 接口
public interface Moveable { public void move(); }
Moveable 接口的实现类 Car
public class Car implements Moveable { @Override public void move() { System.out.println("飙车中..."); } }
生成的代理类 $Proxy0, 其路径: bin/proxy/$Proxy0.java
package proxy; import java.lang.reflect.Method; import java.lang.reflect.InvocationHandler; public class $Proxy0 implements proxy.Moveable{ private InvocationHandler h; public $Proxy0(InvocationHandler h) { this.h = h; } @Override public void move() { try { Method method = proxy.Moveable.class.getMethod("move"); h.invoke(this, method, null); // 暂时只支持无参方法 } catch (Throwable e) { e.printStackTrace(); } } }
总结:
调用 Proxy 实例的方法时, 都会被 InvocationHandler 的实例对象 invoke() 方法所捕获
why?
因为生成的动态类 $Proxy0, 重写了被代理类 Car 的 move() 方法, 在 move() 方法里, 都是在调用InvocationHandler 实例对象的 invoke() 方法