1. 程式人生 > >動態代理的使用和實現機制

動態代理的使用和實現機制

provide imp .com 獲取 機制 代理類 long .html pack

工作中很久沒有接觸動態代理,之前的學習也有些模糊,導致有些遺忘,這裏記錄下個人對動態代理的理解,如有讀者發現問題多多指正吧。

就java而言對於動態代理的支持多是以接口實現,其實現主要是通過java.lang.reflect.Proxy類,java.lang.reflect.InvocationHandler接口。Proxy類主要用於獲取動態代理對象,InvocationHandler接口用來約束調用者實現。

動態代理運行機制:

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法會返回一個代理對象類的實例。當程序執行時會通過反射機制動態的生成一個代理類,該類實現一個接口裏的方法(也就是說代理類與被代理類有相同的接口),在該代理類裏面有一個InvocationHandler類型的成員變量,也就是調用處理程序,通過調用處理程序來給被代理類增強功能。創建好代理類後就調用類加載器將該類加載到類存,然後再通過反射創建一個該代理類的實例對象。下面是具體實現:

1.代理接口:Moveable.java

package com.test;

public interface Moveable {

void move();

}

2.被代理對象:Tank.java
package com.test;

import java.util.Random;

public class Tank implements Moveable {

public void move() {
System.out.println("Tank moving...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}

3.為被代理對象產生一個代理類對象,其中是想增加記錄運行時間的功能

package com.test;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class Proxy {
public static Object newProxyInstance(Class interfaces,InvocationHandler h)throws Exception{
StringBuffer methodStr = new StringBuffer();
String tr = "\r\n";
Method[] methods = interfaces.getMethods();
//拼接代理類的方法
for (Method method : methods) {
methodStr.append(
" public "+ method.getReturnType()+ " " +method.getName()+"() {" + tr +
" try {" + tr +
" java.lang.reflect.Method md = " + interfaces.getName() + "." + "class.getMethod(\"" + method.getName() + "\");" + tr +
" h.invoke(this,md);" + tr +
" }catch(Exception e) {e.printStackTrace();}" + tr +
" }" + tr
);
}

//拼接代理類
String src = "package com.test;" + tr +
"import com.test.Moveable;" + tr +
"public class TimeProxy implements " + interfaces.getName() + " {" + tr +
" private com.test.InvocationHandler h;" + tr +
" public TimeProxy(com.test.InvocationHandler h) {" + tr +
" this.h = h;" + tr +
" }" + tr +
methodStr.toString() + tr +
"}";
//創建代理類
String fileName = System.getProperty("user.dir") + "/src/com/test/TimeProxy.java";
File file = new File(fileName);
FileWriter writer = new FileWriter(file);
writer.write(src);
writer.flush();
writer.close();
//編譯
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
CompilationTask ct = compiler.getTask(null, fileMgr, null, null, null, units);
ct.call();
fileMgr.close();
//加載類到內存:
Class c = ClassLoader.getSystemClassLoader().loadClass("com.test.TimeProxy");
Constructor constructor = c.getConstructor(InvocationHandler.class); //得到參數為InvocationHandler類型的構造方法
Object m = constructor.newInstance(h); //通過該構造方法得到實例
return m;

}
}

4.TankProxy.java

package com.test;

import java.lang.reflect.Method;

public class TankProxy {
public static <T> T getBean(final Object tank) throws Exception{
return (T)Proxy.newProxyInstance(tank.getClass().getInterfaces()[0], new InvocationHandler(){
public void invoke(Object proxy, Method method) {
long start = System.currentTimeMillis();
System.out.println("start:"+start);
try {
method.invoke(tank, new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("end:"+end);
System.out.println("time:"+(end-start));
}

});
}
}

5.測試程序:

package com.test;

import java.util.List;

import com.extend.Tank2;
import com.extend.Tank3;
import com.juhe.LogProxy;
import com.juhe.TimeProxy;

public class Test {
public static void main(String[] args) throws Exception {
Tank tank = new Tank();
Moveable m = TankProxy.getBean(tank);
m.move();

}

}


執行該程序的結果為:
start:1369121253400
Tank moving...
end:1369121260078
time:6678


動態生成的代理類的內容如下:

package com.test;
import com.test.Moveable;
public class TimeProxy implements com.test.Moveable {
private com.test.InvocationHandler h;
public TimeProxy(com.test.InvocationHandler h) {
this.h = h;
}
public void move() {
try {
java.lang.reflect.Method md = com.test.Moveable.class.getMethod("move");
h.invoke(this,md);
}catch(Exception e) {e.printStackTrace();}
}

}

小結:動態代理在運行期通過接口動態生成代理類,這為其帶來了一定的靈活性,但這個靈活性卻帶來了兩個問題,第一代理類必須實現一個接口,如果沒實現接口會拋出一個異常。第二性能影響,因為動態代理使用反射的機制實現的,首先反射肯定比直接調用要慢,其次使用反射大量生成類文件可能引起Full GC造成性能影響,因為字節碼文件加載後會存放在JVM運行時區的方法區(或者叫持久代)中,當方法區滿的時候,會引起Full GC,所以當你大量使用動態代理時,可以將持久代設置大一些,減少Full GC次數。

引用前者:殘劍

動態代理的使用和實現機制