關於java動態代理源碼的探索

本博客閱讀的前提:瞭解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這個對象是如何被這個動態生成的代理對象所使用的,本人還沒有弄清楚!如果有大神可以指點一二,感激不盡!!

發佈了86 篇原創文章 · 獲贊 144 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章