標籤:動態代理
如有錯誤請指正
1. 動態代理的作用:
1. 虛擬機器產生的動態代理對象可以輕鬆地對原有方法進行各種重寫
2. 若沒有動態代理,想實現重寫,必須做一個繼承基類的子類
2. 執行個體示範:
package com.didi.test;
public interface Person {
String skill();
String play();
}
package com.didi.test;
public class Programmer implements Person{
@Override
public String skill() {
System.out.println("coding-------------------------");
return "調用skill方法";
}
@Override
public String play() {
return null;
}
}
package com.didi.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
Programmer programmer;
public MyInvocationHandler(Programmer programmer) {
this.programmer = programmer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before------------");
System.out.println(proxy.getClass().getName());
Object obj = method.invoke(programmer);
System.out.println("after----------------------");
if(!method.getName().equals("skill")){
return "調用娛樂方法";
}
return obj;
}
}
package com.didi.test;
import java.lang.reflect.Proxy;
public class ProxTest {
public static void main(String[] args) {
Programmer p = new Programmer();
Person programmerProxy = (Person)Proxy.newProxyInstance(
ProxTest.class.getClassLoader(),
p.getClass().getInterfaces(),
new MyInvocationHandler(p));
System.out.println("動態代理類的父類:" + programmerProxy.getClass().getSuperclass().getName());
String mes = programmerProxy.play();
System.out.println(mes);
}
}
控制台列印:
動態代理類的父類:java.lang.reflect.Proxy
before------------
com.sun.proxy.$Proxy0
after----------------------
調用娛樂方法
3. 底層實現原理
1. 如下源碼:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
2. 源碼解析
Proxy.newProxyInstance產生代理類對象,這個方法做了兩件重要的事情,首先產生實現傳入介面的實作類別,然後通過反射建立對象,建立對象時傳入一個參數,即我們自己實現的MyInvocationHandler
3. 具體運行情況
當運行代理對象的方法時,虛擬機器做如下處理:
1.收集三個參數:代理對象的引用,當前調用方法的方法名,方法的參數
2.調用MyInvocationHandler的invoke方法,參數為上一步收集的參數
4. 實際應用
1. 日誌,交易管理
2. 遠端程序呼叫RPC,遠程調用時,具體實現在服務端,這時我們在invoke中實現通訊即可(調用方法時,讓遠端的方法被調用,將結果發揮即可),這一切都是通過invoke中的socket通訊實現的
囉嗦一下:
通過動態代理可以輕鬆地對一個類進行方法增強,改造,自訂
關於Java動態代理的一點想法