1. 先理一下動態代理實現的思路:java
實現功能: 本身定義一個類 Proxy, 經過Proxy的靜態方法 newProxyInstance(Class<T> intface,InvocationHandler h)返回代理對象, intface: 被代理類的接口對象, h: InvocationHandler的實例對象ide
1). 聲明一段動態代理類的源碼( 動態產生代理類 )測試
2). 編譯動態代理類的源碼( JDK Compiler API ), 產生代理類this
3). 經過 ClassLoader 加載這個代理類, 建立一個代理類的實例對象代理
4). return 返回這個代理對象code
2. 代碼實現: 對象
爲何代理類的類名爲 $Proxy0?接口
這是由於 Java中動態代理, 生成的代理類的類名就是 $Proxy0, 依葫蘆畫瓢而已, 字符串
能夠用一個動態代理對象 proxy, 來驗證:get
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() 方法