jdk中的動態代理源碼分析

前言

圖片來自https://www.cnblogs.com/maohuidong/p/7992894.html

代理是一種常用的設計模式,其目的就是爲其他對象提供一個代理以控制對這個對象的訪問。爲了保持行爲的一致性,代理類和委託類通常會實現相同的接口,所以在訪問者看來兩者沒有絲毫的區別。通過代理類這中間一層,能有效控制對委託類對象的直接訪問,也可以很好地隱藏和保護委託類對象,同時也爲實施不同控制策略預留了空間,從而在設計上獲得了更大的靈活性

正題

首先,我們先來看下代理的實現,直接看Proxy類的介紹,如圖所示,展示了創建代理的兩種方式,第一種是先獲取代理類,然後獲取構造器,最後構造實例;第二種直接就是創建實例,調用更加的方便。

從代理的使用上發現了InvocationHandler、Class、Constructor等,反射構建實例不就是使用的這些東西嗎?難道代理類也是用反射實現的?這裏我們先不做回答,繼續進行分析
Dynamic Proxy

我們先說下動態代理怎麼來的哈!我們還有中代理叫靜態代理,所謂靜態代理就是將接口、實現類、代理類都手動的完成,比如:
 

public interface People {

    void position();

}

public class TeacherPeople implements People {

    @Override
    public void position() {
        System.out.println("teacher");
    }
}

public class PeopleProxy implements People {

    private People people = new TeacherPeople();

    @Override
    public void position() {
        System.out.println("before method");
        people.position();
        System.out.println("after method");
    }

    @Test
    public void proxyTest(){
        PeopleProxy  peopleProxy = new PeopleProxy();
        peopleProxy.position();
    }
}


如果我們每需要代理一個類就需要寫一個代理就會產生很多的重複代理,因此就有了動態代理,在運行的過程中,動態的創建我們的代理類來完成相關的操作

整個動態代理的框架我在網上找了一個圖,來自於:https://blog.csdn.net/qq_36285943/article/details/80152640

走進Proxy.newProxyInstance方法,先看前半部分的生成代理類:

那到底是怎樣的生成的代理類,走進getProxyClass0查看:

我們先進入proxyClassCache看下這是個什麼東西

proxyClassCache維護了一個subKeyFacory和valueFacory,且都是BiFunction類型的,我們分別看下KeyFactory和ProxyClassFactory

KeyFactory

ProxyClassFactory

apply的代碼比較長,我篩選出幾行關鍵的代碼

@Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //獲取每個代理的接口的Class對象,存到set集合中
            for (Class<?> intf : interfaces) {
               
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }

              //確定包以及權限相關的操作吧
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
             //原子遞增屬於代理類名的一部分
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            //生成代理類的class文件,返回最後的Class
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
               
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

看完了WeakCache的構造,我們繼續回到get方法:return proxyClassCache.get(loader, interfaces);

Factory與Supplier的關係也就是圖中這樣,其實也就是Factory

通過這一串的分析我們我們已經獲取到了要生成代理的Factory,然後調用get方法去生成ProxyClass,生成的方法就是上面我們分析的ProxyClassFactory的apply方法

這裏有點繞,各位一定要自己進入代碼查看分析,通過一連串的方法我們就拿到了代理類的Class對象,也就是Proxy。newinstance的前部分 ,然後我們再來看後半部分,後半部分就簡單多了。

 

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