天天看點

Java 動态代理Proxy.newProxyInstance()

文章目錄

  • ​​靜态代理​​
  • ​​動态代理​​
  • ​​小結​​

靜态代理

定義接口:

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幫我們自動編寫了一個上述類(不需要源碼,可以直接生成位元組碼),并不存在可以直接執行個體化接口的黑魔法。

小結