文章目錄
- 靜态代理
- 動态代理
- 小結
靜态代理
定義接口:
public interface Hello {
void morning(String name);
}
編寫實作類:
public class HelloWorld implements Hello {
public void morning(String name) {
System.out.println("Good morning, " + name);
}
}
建立執行個體,轉型為接口并調用:
Hello hello = new HelloWorld();
hello.morning("Bob");
這種方式就是我們通常編寫代碼的方式。
動态代理
還有一種方式是動态代碼,我們仍然先定義了接口
Hello
,但是我們并不去編寫實作類,而是直接通過
JDK
提供的一個
Proxy.newProxyInstance()
建立了一個
Hello
接口對象。這種沒有實作類但是在運作期動态建立了一個接口對象的方式,我們稱為動态代碼。
JDK
提供的動态建立接口對象的方式,就叫動态代理。
一個最簡單的動态代理實作如下:
public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 傳入ClassLoader
new Class[] { Hello.class }, // 傳入要實作的接口
handler); // 傳入處理調用方法的InvocationHandler
hello.morning("Bob");
}
}
interface Hello {
void morning(String name);
}
在運作期動态建立一個interface執行個體的方法如下:
- 定義一個InvocationHandler執行個體,它負責實作接口的方法調用;
-
通過Proxy.newProxyInstance()建立interface執行個體,它需要3個參數:
1、使用的ClassLoader,通常就是接口類的ClassLoader;
2、需要實作的接口數組,至少需要傳入一個接口進去;
3、用來處理接口方法調用的InvocationHandler執行個體。
- 将傳回的Object強制轉型為接口
動态代理實際上是JVM在運作期動态建立class位元組碼并加載的過程,它并沒有什麼黑魔法,把上面的動态代理改寫為靜态實作類大概長這樣:
public class HelloDynamicProxy implements Hello {
InvocationHandler handler;
public HelloDynamicProxy(InvocationHandler handler) {
this.handler = handler;
}
public void morning(String name) {
handler.invoke(
this,
Hello.class.getMethod("morning", String.class),
new Object[] { name });
}
}
其實就是JVM幫我們自動編寫了一個上述類(不需要源碼,可以直接生成位元組碼),并不存在可以直接執行個體化接口的黑魔法。