本博客閱讀的前提:瞭解Class對象,可以參考:https://blog.csdn.net/river66/article/details/103606336
java實現動態代理利用的是java的反射機制,我們會使用Proxy.newProxyInstance()生成代理類。如:
public interface Movable {
void move();
}
public class Mouse implements Movable {
@Override
public void move() {
System.out.println("老鼠跑得快!");
}
}
final Movable movable = new Mouse();
Class<?> movableClass = movable.getClass();
Movable proxy = (Movable) Proxy.newProxyInstance(movableClass.getClassLoader(),
movableClass.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("貓抓老鼠!");
Object result = method.invoke(movable, args);
System.out.println("老鼠還是被抓到了!");
return result;
}
});
proxy.move();
打印:
貓抓老鼠!
老鼠跑得快!
老鼠還是被抓到了!
要使用java自帶的api完成動態代理,前提條件就是目標類必須實現接口!(如上面的Movable接口)
還有一種動態代理可以不用實現接口,那就是Cglib動態代理,它的原理是生成目標類的子類當做代理類。
因爲代理類對象是需要強轉爲目標類對象以供程序使用的,所以要麼是使用接口持有目標類對象的引用,要麼是子類強轉爲父類(滿足里氏代換原則)
下面重點分析Proxy.newProxyInstance()函數中,是如何利用java的反射機制實現的???(未搞定)
根據分析源碼,我提取了關鍵的代碼如下:
//爲了使用Proxy類中的私有方法:defineClass0(Native方法),需要利用反射技術來進行調用
Class proxyClass = Class.forName("java.lang.reflect.Proxy");
Constructor[] constructors = proxyClass.getDeclaredConstructors();
constructors[0].setAccessible(true);
Proxy proxy = (Proxy) constructors[0].newInstance();
Method method = proxyClass.getDeclaredMethod("defineClass0", ClassLoader.class, String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
String proxyName = "com.river.MyProxy";
Movable mouse = new Mouse();
Class mouseClass = mouse.getClass();
Class<?>[] intfs = mouseClass.getInterfaces().clone();
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
//動態生成com.river.MyProxy.class文件對應的字節數據
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, intfs, accessFlags);
//調用defineClass0方法,生成com.river.MyProxy.class對應的Class對象。
Class mouseProxyClass = (Class) method.invoke(proxy, mouseClass.getClassLoader(), proxyName, proxyClassFile, 0, proxyClassFile.length);
System.out.println("生成的代理類對應的Class對象:"+mouseProxyClass.getName());
Class<?>[] constructorParams = { InvocationHandler.class };
Constructor<?> cons = mouseProxyClass.getConstructor(constructorParams);
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"被調用");
//注意:這裏調用method.invoke()是會發生錯誤的!!
return null;
}
};
//必須將h對象通過構造函數傳入,這樣該對象的invoke方法纔會被動態生成的代理對象所調用
Movable proxyMouse = (Movable) cons.newInstance(new Object[]{h});
proxyMouse.move();
打印:
生成的代理類對應的Class對象:com.river.MyProxy
move被調用
上面的例子雖然可以跑起來,但是邏輯是不對的,只是爲了演示大概流程。因爲生成.class字節數據和defineClass0()方法的實現細節,以及InvocationHandler h這個對象是如何被這個動態生成的代理對象所使用的,本人還沒有弄清楚!如果有大神可以指點一二,感激不盡!!