jdk動態代理的簡單原理

首先看看代理模式的基本類圖:此圖來源於:https://juejin.im/post/5c1ca8df6fb9a049b347f55c

在這裏插入圖片描述

我們最主要的目的是調用RealSubject的request方法,但是在實際過程中,我們可能需要在這個方法之前前後做一些別的邏輯處理,爲了不做代碼侵入或者有些時候我們根本沒有辦法修改RealSubject的源碼,這個時候採用代理模式就非常有效,通過創建一個代理類Proxy,然後使用這個類調用真實類RealSubject的request方法

本文主要是想講講jdk動態代理的基本原理,爲什麼我們代理類實現了InvocationHandler接口的invoke()方法後,然後通過Proxy.newInstance(loader, interface[], realSubject)就能實現調用,內部具體是怎麼實現調用的,我們並沒調用invoke方法不是?

首先還是看看簡單樣例代碼

public interface TestImp {
    void test();
}


public class TestImpl implements TestImp {

    @Override
    public void test() {
        System.out.println("test Handler impl");
    }
}

public class TestHandler implements InvocationHandler {
    private Object object;

    public TestHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object res = method.invoke(object, args);
        after();
        return res;
    }

    public void before(){
        System.out.println("testImp before");
    }

    public void after(){
        System.out.println("testImp after");
    }
}

//測試類
public class Test {
    public static void main(String[] args) throws InterruptedException {
        TestImp testImp = new TestImpl();
        TestHandler handler = new TestHandler(testImp);
        TestImp imp = (TestImp) Proxy.newProxyInstance(TestImp.class.getClassLoader(), TestImpl.class.getInterfaces(), handler);
        ProxyUtils.generateClassFile(testImp.getClass(), "$Proxy1");
            imp.test();
    }
}


//這段是最後生成代理類的test()方法,後邊會說明這段代碼是怎麼生成出來的
public final void test() throws  {
    try {
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

//這段代碼是網上找的,用於答應生成的代理類信息
//來源於:https://juejin.im/post/5c1ca8df6fb9a049b347f55c
public class ProxyUtils {
    /**
     * 將根據類信息動態生成的二進制字節碼保存到硬盤中,默認的是clazz目錄下
     * params: clazz 需要生成動態代理類的類
     * proxyName: 爲動態生成的代理類的名稱
     */
    public static void generateClassFile(Class clazz, String proxyName) {
        // 根據類信息和提供的代理類名稱,生成字節碼
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;
        try {
            //保留到硬盤中
            out = new FileOutputStream(paths + proxyName + ".class");
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

以上代碼爲什麼這麼寫不解釋,我們的目的是看看Proxy.newProxyInstance(TestImp.class.getClassLoader(), TestImpl.class.getInterfaces(), handler);這段代碼的內部實現

其實這段代碼意思很明確,但是具體內部實現細節我們還是不知道,只是它告訴我們這裏會生成一個我們傳入的具體需要代理的實現類的代理類對象出來

就是身材上圖中RealSubject的一個代理類對象出來

然後通過構造方法傳入我們實現的InvocationHander對象,然後創建除代理對象實例,

其實我們最關心就是這個代理對象類是怎麼創建出來的,

Class<?> cl = getProxyClass0(loader, intfs);這纔是核心

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //檢查handler參數不能爲空,
        Objects.requireNonNull(h);
        //克隆一份具體實現類的class對象
        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.
         * 調用handler的構造構造方法
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            //拿到構造方法,具體實現類的,這個地方也是關鍵呀
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            //將我們自定以的handler賦值給ih
            final InvocationHandler ih = h;
            //判斷生成的代理是不是public的,如果不是將設置構造方法accessible
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //生成新的實例返回, 這個地方值得注意了,需要傳入一個參數,這個蠶食是InvocationHandler數組
            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);
        }
    }

代理類Class生成方法,但是這裏也只是一個方法,並不是我們實際關心的地方

return proxyClassCache.get(loader, interfaces);繼續深入

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        //loader: 我們定義的接口的類加載器,
        //interfaces 表示這個接口的具體實現類

        //具體實現類數組的長度不能大於65535
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //如果這個類已經在給定加載器中生成過了,那麼直接返回一個緩存就好了, 否則就要生成一個新的代理類
        return proxyClassCache.get(loader, interfaces);
    }

proxyClassCache 是一個WeakCache, 內部有兩個核型屬性;

public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                 BiFunction<K, P, V> valueFactory) {
    this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
    this.valueFactory = Objects.requireNonNull(valueFactory);
}

最後其實我們需要的就是valueFactory屬性的解析

首先說一下緩存結構,這個map就是用於對loader生成的Class進行緩存的,所以第一個Object表示的是ClassLoader, 而第二個map用戶存儲具體生成的Class信息,而第二個Object是根據interface生成的subKey,Supplier就是最後我們需要解析的,我們Class也是通過這個Supplier的get()方法得到的,後邊對於緩存的代碼不解釋了,直接去看Class身材的核心代碼,不過這部分代碼oracle公司並沒有開源

private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
    = new ConcurrentHashMap<>();
public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        //
        expungeStaleEntries();

        //拿到緩存
        //CacheKey 是weakReference的繼承類,
        //這裏如果key == null ,那麼返回一個object對象回來
        //否則創建一個新的CacheKey對象回來
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        //如果當前類加載器沒有進行緩存,那麼就創建一個新的ConcurrentHashMap對該類加載的類進行緩存
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        //subKeyFactory: new KeyFactory()
        //key 傳入進去沒有作用, subKey更加實現實現類的不同,分別是Key1, Key2, Key0 或者keyX
        //這些Key都是reference的實現
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

        //判斷在緩存(ConcurrentHashMap)是否存在當前代理類具體實現類的緩存,
        //如果存在緩存, 那麼可以直接通過supplier.get()拿到我們最終需要的代理代理實現類
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            //如果緩存存在,那麼直接拿到這個緩存的代理對象類返回
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            //如果緩存不存在,這裏創建一個Factory用戶生成代理實現類,內部通過ProxyClassFacotry具體實類生成
            // lazily construct a Factory
            if (factory == null) {
                //key 類加載器,subKey是實現類轉換得到的key, patameter實現類 , valuesMap是代理實現類的hashMap緩存
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            //如果supplier爲空,也就說當前還沒有緩存,那麼就將剛纔生成緩存對象賦值給supplier
            if (supplier == null) {
                //將factory放入到緩存map中,如果得到的supplier,說明真的沒有緩存,這個時候就可以進行賦值了
                //這裏這樣可以有效的避免多線程安全問題
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                //當上邊的supplier不爲空的時候,但有沒有拿到緩存的代理實現類,那麼就需要進行緩存更換
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

ProxyClassFactory類是WeakCache中兩個核心屬性之一,最後生成Class便是從它開始進入的,而緩存也是着這個方法的apply方法實現的,其實有點不明白,說是緩存,但是好像並沒有做大,沒錯都會調用這個apply方法

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); 字節碼生成方法入口

private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // prefix for all proxy class names
    private static final String proxyClassNamePrefix = "$Proxy";

    // next number to use for generation of unique proxy class names
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

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

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            /*
             * Verify that the class loader resolves the name of this
             * interface to the same Class object.
             */
            Class<?> interfaceClass = null;
            try {
                //拿到具體實現類的類class對象
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * Verify that the Class object actually represents an
             * interface.
             * 驗證是不是一個接口,如果不是,那麼就拋出一樣,表示他沒有實現一個接口
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * Verify that this interface is not a duplicate.
             * 不能重複操作
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        //代理類包名
        String proxyPkg = null;     // package to define proxy class in
        //類訪問表示,這個可以接口深入理解jvm中類加載章節進行理解,表示爲public final
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
         * Record the package of a non-public proxy interface so that the
         * proxy class will be defined in the same package.  Verify that
         * all non-public proxy interfaces are in the same package.
         */
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();

            //判斷實現類是不是public的,如果不是進行如下操作
            if (!Modifier.isPublic(flags)) {
                //如果不是public,那麼將訪問表示直接調整爲final
                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");
                }
            }
        }

        //如果沒有拿到包名稱, 那麼就使用默認的包名稱
        //com.sun.proxy
        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         * Choose a name for the proxy class to generate.
         * 拿到生成代理類的名稱, $Proxy + x
         */
        long num = nextUniqueNumber.getAndIncrement();
        //代理類名稱
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * Generate the specified proxy class.
         * 生成代理類的字節碼,
         * 內部細節:
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);

        try {
            //返回定義的類
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
            throw new IllegalArgumentException(e.toString());
        }
    }
}

最後調用ProxyGenerator方法的generateClassFile方法,生成我們需要的Class字節碼,從這個方法中可以找到有關這個代理類的構造方法,需要傳入一個InvocationHandler實例

還有一個更爲重要的方法就是generateMethod(),這個方法可以纔是核心中核心,其實它就是一個方法生成方法,但是就是在這個方法中定義我我們調用的邏輯爲

super.h.invoke(this, m3, (Object[])null);

private byte[] generateClassFile() {
    this.addProxyMethod(hashCodeMethod, Object.class);
    this.addProxyMethod(equalsMethod, Object.class);
    this.addProxyMethod(toStringMethod, Object.class);
    Class[] var1 = this.interfaces;
    int var2 = var1.length;

    int var3;
    Class var4;
    for(var3 = 0; var3 < var2; ++var3) {
        var4 = var1[var3];
        Method[] var5 = var4.getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method var8 = var5[var7];
            this.addProxyMethod(var8, var4);
        }
    }

    Iterator var11 = this.proxyMethods.values().iterator();

    List var12;
    while(var11.hasNext()) {
        var12 = (List)var11.next();
        checkReturnTypes(var12);
    }

    Iterator var15;
    //添加構造方法,進入這個方法,我們可以看到構造方法需要傳入InvocationHandler對象數組,然後最開是我們的newInstance方法中傳入的InvocationHandler對象也就會被用到這個構造方法中
    try {
        this.methods.add(this.generateConstructor());
        var11 = this.proxyMethods.values().iterator();

        while(var11.hasNext()) {
            var12 = (List)var11.next();
            var15 = var12.iterator();

            while(var15.hasNext()) {
                ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                this.methods.add(var16.generateMethod());
            }
        }

        this.methods.add(this.generateStaticInitializer());
    } catch (IOException var10) {
        throw new InternalError("unexpected I/O Exception", var10);
    }

    if (this.methods.size() > 65535) {
        throw new IllegalArgumentException("method limit exceeded");
    } else if (this.fields.size() > 65535) {
        throw new IllegalArgumentException("field limit exceeded");
    } else {
        this.cp.getClass(dotToSlash(this.className));
        this.cp.getClass("java/lang/reflect/Proxy");
        var1 = this.interfaces;
        var2 = var1.length;

        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            this.cp.getClass(dotToSlash(var4.getName()));
        }

        this.cp.setReadOnly();
        ByteArrayOutputStream var13 = new ByteArrayOutputStream();
        DataOutputStream var14 = new DataOutputStream(var13);

        try {
            var14.writeInt(-889275714);
            var14.writeShort(0);
            var14.writeShort(49);
            this.cp.write(var14);
            var14.writeShort(this.accessFlags);
            var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
            var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
            var14.writeShort(this.interfaces.length);
            Class[] var17 = this.interfaces;
            int var18 = var17.length;

            for(int var19 = 0; var19 < var18; ++var19) {
                Class var22 = var17[var19];
                var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
            }

            var14.writeShort(this.fields.size());
            var15 = this.fields.iterator();

            while(var15.hasNext()) {
                ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                var20.write(var14);
            }

            var14.writeShort(this.methods.size());
            var15 = this.methods.iterator();

            while(var15.hasNext()) {
                ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                var21.write(var14);
            }

            var14.writeShort(0);
            return var13.toByteArray();
        } catch (IOException var9) {
            throw new InternalError("unexpected I/O Exception", var9);
        }
    }
}

這個方法就是將我們的realSubject.request方法轉換成h.invoke()方法過程,由於oracle公司並沒有開源這部分代碼,讀起來比較困難,我也理解了一個大概

private ProxyGenerator.MethodInfo generateMethod() throws IOException {
    String var1 = ProxyGenerator.getMethodDescriptor(this.parameterTypes, this.returnType);
    ProxyGenerator.MethodInfo var2 = ProxyGenerator.this.new MethodInfo(this.methodName, var1, 17);
    int[] var3 = new int[this.parameterTypes.length];
    int var4 = 1;

    for(int var5 = 0; var5 < var3.length; ++var5) {
        var3[var5] = var4;
        var4 += ProxyGenerator.getWordsPerType(this.parameterTypes[var5]);
    }

    byte var7 = 0;
    DataOutputStream var9 = new DataOutputStream(var2.code);
    ProxyGenerator.this.code_aload(0, var9);
    var9.writeByte(180);
    var9.writeShort(ProxyGenerator.this.cp.getFieldRef("java/lang/reflect/Proxy", "h", "Ljava/lang/reflect/InvocationHandler;"));
    ProxyGenerator.this.code_aload(0, var9);
    var9.writeByte(178);
    var9.writeShort(ProxyGenerator.this.cp.getFieldRef(ProxyGenerator.dotToSlash(ProxyGenerator.this.className), this.methodFieldName, "Ljava/lang/reflect/Method;"));
    if (this.parameterTypes.length > 0) {
        ProxyGenerator.this.code_ipush(this.parameterTypes.length, var9);
        var9.writeByte(189);
        var9.writeShort(ProxyGenerator.this.cp.getClass("java/lang/Object"));

        for(int var10 = 0; var10 < this.parameterTypes.length; ++var10) {
            var9.writeByte(89);
            ProxyGenerator.this.code_ipush(var10, var9);
            this.codeWrapArgument(this.parameterTypes[var10], var3[var10], var9);
            var9.writeByte(83);
        }
    } else {
        var9.writeByte(1);
    }

    var9.writeByte(185);
    var9.writeShort(ProxyGenerator.this.cp.getInterfaceMethodRef("java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"));
    var9.writeByte(4);
    var9.writeByte(0);
    if (this.returnType == Void.TYPE) {
        var9.writeByte(87);
        var9.writeByte(177);
    } else {
        this.codeUnwrapReturnValue(this.returnType, var9);
    }

    short var6;
    short var8 = var6 = (short)var2.code.size();
    List var13 = ProxyGenerator.computeUniqueCatchList(this.exceptionTypes);
    if (var13.size() > 0) {
        Iterator var11 = var13.iterator();

        while(var11.hasNext()) {
            Class var12 = (Class)var11.next();
            var2.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(var7, var8, var6, ProxyGenerator.this.cp.getClass(ProxyGenerator.dotToSlash(var12.getName()))));
        }

        var9.writeByte(191);
        var6 = (short)var2.code.size();
        var2.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(var7, var8, var6, ProxyGenerator.this.cp.getClass("java/lang/Throwable")));
        ProxyGenerator.this.code_astore(var4, var9);
        var9.writeByte(187);
        var9.writeShort(ProxyGenerator.this.cp.getClass("java/lang/reflect/UndeclaredThrowableException"));
        var9.writeByte(89);
        ProxyGenerator.this.code_aload(var4, var9);
        var9.writeByte(183);
        var9.writeShort(ProxyGenerator.this.cp.getMethodRef("java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V"));
        var9.writeByte(191);
    }

    if (var2.code.size() > 65535) {
        throw new IllegalArgumentException("code size limit exceeded");
    } else {
        var2.maxStack = 10;
        var2.maxLocals = (short)(var4 + 1);
        var2.declaredExceptions = new short[this.exceptionTypes.length];

        for(int var14 = 0; var14 < this.exceptionTypes.length; ++var14) {
            var2.declaredExceptions[var14] = ProxyGenerator.this.cp.getClass(ProxyGenerator.dotToSlash(this.exceptionTypes[var14].getName()));
        }

        return var2;
    }
}

總結:

​ 1.首先通過Proxy.newInstance(loader,interface[], handler)進入,然後調用getProxyClass0(loader, intfs)生成Class對象
2. 調用WeakCache對象的get()方法
3. 拿到Loader對應的緩存Map, 生成subkey,判斷是否存在緩存,如果有,取出來返回
4. 沒有緩存就生成Factory,然後調用ProxyClassFactory的apply方法
5. 調用ProxyGenerator.generateProxyClass()方法
6. 調用ProxyGenerator.generateClassFile()方法
7. 生成構造器
8. 實現代理實現類的具體方法,調用generateMethod()方法
9. 最後調用構造方法生成實例返回

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章