JDK動態代理的簡單實現

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() 方法

相關文章
相關標籤/搜索