jdk動態代理和cglib動態代理底層實現原理超詳細解析(jdk動態代理篇)


  代理模式是一種很常見的模式,本文主要分析jdk動態代理的過程

1.舉例

  

public class ProxyFactory implements InvocationHandler {

    private Class target;

    public <T>T getProxy(Class<T> c)  {
        this.target = c;
        return (T)Proxy.newProxyInstance(c.getClassLoader(),c.isInterface()?new Class[]{c}:c.getInterfaces(),this);
    }


    @Override
    public Object invoke(Object proxy , Method method , Object[] args) throws Throwable {
        System.out.println("代理執行執行");
        if ( !target.isInterface() ){
            method.invoke(target.newInstance(),args);
        }
        return "代理返回值";
    }

    public static void main(String[] args) {
        // 保存生成的代理類的字節碼文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        ProxyFactory proxyFactory = new ProxyFactory();
        IProx proxyImpl = proxyFactory.getProxy(ProxImpl.class);
        String result = proxyImpl.hello("hello word");
        System.out.println(result);
        System.out.println("---------");
        IProx proxy = proxyFactory.getProxy(IProx.class);
        result = proxy.hello("hello word");
        System.out.println(result);
    }


}

interface IProx{
    String hello(String id);
}
class ProxImpl implements IProx{

    @Override
    public String hello(String id) {
        System.out.println(id);
        return null;
    }
}

執行main方法后結果如下

 

 

 

可以看到定義的hello方法已經被執行,並且可以在不定義接口的實現類的時候仍然可以執行方法獲取結果,這其實就很容易想到mybatis中直接調用mapper接口獲取查詢結果其實也是調用的mapper的動態代理類,說明動態代理對於構造框架有很重要的作用

 

原理解析

1.Proxy.newProxyInstance方法

我們可以看到構造代理類的核心方法為這句 三個參數分別為代理類的類加載器,代理類的所有接口,如果本身就是接口則直接傳入本身,傳入InvocationHandler接口的實現類

Proxy.newProxyInstance(c.getClassLoader(),c.isInterface()?new Class[]{c}:c.getInterfaces(),this);

直接進入方法中查看

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)throws IllegalArgumentException{
        //InvocationHandler不能為null
        Objects.requireNonNull(h);
       //克隆出所有傳入的接口數組
        final Class<?>[] intfs = interfaces.clone();
        
        //權限校驗
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 查找或者生成代理類  核心邏輯  參數為類加載器和上面的接口數組
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /************************下面的代碼邏輯先不用管 到后面會專門分析***************************/
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //拿到其構造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //通過構造方法新建類的實例並返回
            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<?> cl = getProxyClass0(loader, intfs);

2.getProxyClass0方法

繼續進入方法getProxyClass0(loader,intfs)

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        return proxyClassCache.get(loader, interfaces);
    }

可以看到方法比較簡單,主要對接口數量做了個判斷,然后通過緩存proxyClassCache獲取代理類,這兒注意proxyClassCache在初始化時已經初始化了一個KeyFactory和ProxyClassFactory

這個后面創建代理類時將會用到

    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

 

我們繼續查看proxyClassCache的get方法

 

3.WeakCache的get方法

    // key為加載器  P為接口數組
    public V get(K key, P parameter) {
        //接口不能為空
        Objects.requireNonNull(parameter);
        //清理舊的緩存
        expungeStaleEntries();
        //構造緩存的ClassLoader key
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        //通過緩存的map查找
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            //如果值為空  則put一個新的空值進去 
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            //再次確認 應該是防止並發狀況如果已經有值則將valuesMap賦值為已有的值
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        //拿到該代理類的classKey
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        //通過代理類的key查找對應的緩存Supplier
        Supplier<V> supplier = valuesMap.get(subKey);
        //factory為supplier的實現類
        Factory factory = null;

        while (true) {
            //如果Supplier不為空
            if (supplier != null) {
                //直接返回值
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            //如果代理類創建工廠為空
            if (factory == null) {
                //則新建一個創建工廠
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
            //如果supplier為空
            if (supplier == null) {
                //將當前的代理類key   以及新建的Factory存到緩存里面去
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // 這個時候將supplier設置為factory
                    supplier = factory;
                }
                //如果supplier不為空  則用這個supplier替代之前的代理類key的值
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    //將supplier設置為factory實現類
                    supplier = factory;
                } else {
                    //沒有替換成功則直接返回緩存里面有的
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

4.Factory的get方法

  通過上面的邏輯可以得知  最后是新建了Factory factory = new Factory(key, parameter, subKey, valuesMap); 並將這個factory賦值給Suplier調用get方法返回構造的代理類。所以我們直接看

Factory的get方法即可

        @Override
        public synchronized V get() { // serialize access
            // //即再次確認 對應的類代理key的supplier有沒有被更改
            Supplier<V> supplier = valuesMap.get(subKey);
            //如果被更改了則返回null
            if (supplier != this) {
                return null;
            }
            
            V value = null;
            try {
                //創建value
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { 
                    //如果value為null  則代表當前的supplier有問題 所以直接移除
                    valuesMap.remove(subKey, this);
                }
            }
            //再次確認value即返回的代理類不能為null
            assert value != null;

            // 將返回的代理類包裝為一個緩存
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // 將這個包裝緩存存入緩存map
            reverseMap.put(cacheValue, Boolean.TRUE);

            // 替換當前代理類key的supplier為剛包裝的緩存 后面則可以直接調用緩存  必須成功
            if (!valuesMap.replace(subKey, this, cacheValue)) {
                throw new AssertionError("Should not reach here");
            }
            return value;
        }

  該方法的主要作用則是通過valueFactory創建代理類后  將代理類包裝為CacheValue(注意該類實現了Supplier接口)並將valuesMap緩存中對應代理類的Supplier替換為包裝后的CacheValue,這樣后面就可以直接調用CacheValue的get方法來獲取代理類

  接下來我們開始分析valueFactory.apply(key,parameters),這兒注意上面在初始化weakCache時已經講到,在構造函數中傳入了兩個參數,new KeyFactory(), new ProxyClassFactory(),分別對應subKeyFactory和valueFactory,所以這里的valueFactory則代表ProxyClassFactory,所以我們直接看ProxyClassFactory的apply方法邏輯

 

5.ProxyClassFactory的apply方法

 

 private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
        // 代理類名前綴
        private static final String proxyClassNamePrefix = "$Proxy";

        // 原子long  用來做代理類編號
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            //構建一個允許相同值的key的map
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //遍歷代理類的接口
            for (Class<?> intf : interfaces) {
                
                Class<?> interfaceClass = null;
                try {
                    //根據接口的名字以及傳入的類加載器來構建一個class
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    //如果不同 則說明傳入的類加載器和指定代理方法時的類加載器不是同一個加載器
                    //,根據雙親委派機制和jvm規定,只有同樣的類加載器加載出來的一個類  才確保
                    //為同一個類型   即instance of 為true
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                //確認傳入的接口class數組沒有混入奇怪的東西
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                //將接口存儲上面的interfaceSet中
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
            //聲明代理類的包
            String proxyPkg = null;
            //設置生成代理類的修飾級別  目前是public final
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            
            for (Class<?> intf : interfaces) {
                //獲取class修飾符魔數 
                //這兒接口正常獲取到的值為1536 (INTERFACE 512  +  ABSTRACT1024)
                int flags = intf.getModifiers();
                //如果為接口則為true
                if (!Modifier.isPublic(flags)) {
                    //將accessFlags設置為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");
                    }
                }
            }
            //如果包獲取到的為空  則用系統提供的
            if (proxyPkg == null) {
                // com.sun.proxy.
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            //獲得一個編號( 線程安全)
            long num = nextUniqueNumber.getAndIncrement();
            //構建代理類的名字  例如  com.sun.proxy.$Proxy0
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            //生成代理類的字節碼
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                //根據字節碼生成代理類
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
               
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

  該方法的邏輯則主要得到代理類的包名(一般來說為最后一個接口的包名),以及產生字節碼文件 ,根據字節碼文件生成代理類class並返回,核心的兩個方法為ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);  以及defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);

 

6.generateProxyClass方法

  我們先看字節碼生成的方法,ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);  后面的類不開放源代碼,可以使用idea自帶的反編譯工具查看,我將

參數名字做了些修改  (汗   。。不然var1  var2看的屬實難受 )

public static byte[] generateProxyClass(final String proxyName, Class<?>[] interfaces, int accessFlags) {
        //生成一個生成器實例   並賦予對應的屬性(包名,接口數組,修飾符)
        ProxyGenerator generator = new ProxyGenerator(proxyName, interfaces, accessFlags);
        //生成數組
        final byte[] resultByte = generator.generateClassFile();
        
        //如果saveGeneratedFiles為true  則生成本地文件  這兒不講解 有興趣的可以研究
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int interfaces = proxyName.lastIndexOf(46);
                        Path accessFlags;
                        if (interfaces > 0) {
                            Path generator = Paths.get(proxyName.substring(0, interfaces).replace('.', File.separatorChar));
                            Files.createDirectories(generator);
                            accessFlags = generator.resolve(proxyName.substring(interfaces + 1, proxyName.length()) + ".class");
                        } else {
                            accessFlags = Paths.get(proxyName + ".class");
                        }

                        Files.write(accessFlags, resultByte, new OpenOption[0]);
                        return null;
                    } catch (IOException resultBytex) {
                        throw new InternalError("I/O exception saving generated file: " + resultBytex);
                    }
                }
            });
        }

        return resultByte;
    }

  該方法主要聲明一個生成器,然后調用generateClassFile方法生成byte[]數組,后面的可選則生成本地文件,這個就是一開始main方法中的System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 當然也可以在java啟動參數加上-D傳入參數,我們主要看generateClassFile()方法

 

7.generateClassFile方法

該方法為核心生成方法  每步均有說明,代碼后會附帶一張java字節碼文件構造,可以參照

 

private byte[] generateClassFile() {
        //Map<String, List<ProxyGenerator.ProxyMethod>> proxyMethods = new HashMap(); 用來存儲方法簽名  及其對應的方法描述
        //addProxyMethod方法下面有詳解
        
        
        //添加Object的hashcode方法
        this.addProxyMethod(hashCodeMethod, Object.class);
        //添加Object的equals方法
        this.addProxyMethod(equalsMethod, Object.class);
        //添加Object的toString方法
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] interfaces = this.interfaces;
        //獲取到接口數組的長度
        int interLength = interfaces.length;

        int index;
        Class in;
        for(index = 0; index < interLength; ++index) {
            in = interfaces[index];
            //迭代獲取到當前接口的所有方法
            Method[] methods = in.getMethods();
            int methodLength = methods.length;
            //迭代所有方法
            for(int methodIndex = 0; methodIndex < methodLength; ++methodIndex) {
                Method m = methods[methodIndex];
                //添加方法簽名與方法描述
                this.addProxyMethod(m, in);
            }
        }
        /************上面則就將所有的方法簽名與方法描述都存入到了proxyMethods中********************/
        Iterator methodsIterator = this.proxyMethods.values().iterator();
        
        List meth;
        while(methodsIterator.hasNext()) {
            //迭代每個方法簽名的值 (List<ProxyGenerator.ProxyMethod>)
            meth = (List)methodsIterator.next();
            //檢查方法簽名值一樣的方法返回值是否相同
            checkReturnTypes(meth);
        }
        
        Iterator me;
        try {
            //添加構造方法  並將構造方法的二進制代碼寫入methodInfo中的ByteArrayOutputStream --》對應字節碼文件中的有參構造
            this.methods.add(this.generateConstructor());
            //獲取方法簽名對應迭代器
            methodsIterator = this.proxyMethods.values().iterator();
            
            while(methodsIterator.hasNext()) {
                meth = (List)methodsIterator.next();
                me = meth.iterator();
                //獲取到每個方法簽名中對應描述列表的迭代器
                while(me.hasNext()) {
                    //獲取到方法信息
                    ProxyGenerator.ProxyMethod proMe = (ProxyGenerator.ProxyMethod)me.next();
                    //在feilds添加方法對應參數名字 簽名,以及訪問修飾符的FiledInfo
                    this.fields.add(new ProxyGenerator.FieldInfo(proMe.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    //添加對應的方法  --》對應字節碼文件中的代理類接口實現方法
                    this.methods.add(proMe.generateMethod());
                }
            }  
            //添加對應的靜態方法  --》對應字節碼文件中的static方法
            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));
            //常量池中符號引用保存繼承的Proxy類的權限定名
            this.cp.getClass("java/lang/reflect/Proxy");
            //獲取到所有的接口
            interfaces = this.interfaces;
            //獲取到接口的長度
            interLength = interfaces.length;
            //遍歷接口
            for(index = 0; index < interLength; ++index) {
                in = interfaces[index];
                //常量池中符號引用保存接口的全限定定名
                this.cp.getClass(dotToSlash(in.getName()));
            }
            //常量池符號引用中保存訪問標志
            this.cp.setReadOnly();
            //創建輸出流
            ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
            DataOutputStream outputStream = new DataOutputStream(byteOutputStream);

            try {
                //1.寫入java魔數4個字節 對應16進制數據為JAVA魔數CA FE BA BE
                outputStream.writeInt(-889275714);
                //2..寫入小版本號2個字節
                outputStream.writeShort(0);
                //3.寫入大版本號2個字節
                outputStream.writeShort(49);
                //4.寫入常量池計數2個字節 5.以及上面添加的常量池
                this.cp.write(outputStream);
                //6.設置訪問標志 2個字節
                outputStream.writeShort(this.accessFlags);
                //7.設置類索引2個字節
                outputStream.writeShort(this.cp.getClass(dotToSlash(this.className)));
                //8.設置父類索引 2個字節   這兒繼承Proxy類  所以只能代理接口 因為java單繼承
                outputStream.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                //9.設置接口長度 2個字節  所以前面要判斷接口數量不能大於65535
                outputStream.writeShort(this.interfaces.length);
                Class[] interfacess = this.interfaces;
                int interfacelength = interfacess.length;

                for(int index = 0; index < interfacelength; ++index) {
                    Class c = interfacess[index];
                    //10.寫入接口索引 2個字節
                    outputStream.writeShort(this.cp.getClass(dotToSlash(c.getName())));
                }
                //11.寫入變量數量 2個字節
                outputStream.writeShort(this.fields.size());
                me = this.fields.iterator();
                
                while(me.hasNext()) {
                    ProxyGenerator.FieldInfo p = (ProxyGenerator.FieldInfo)me.next();
                    //12.寫入變量
                    p.write(outputStream);
                }
                //13.寫入方法數量 2個字節
                outputStream.writeShort(this.methods.size());
                me = this.methods.iterator();

                while(me.hasNext()) {
                    ProxyGenerator.MethodInfo proMe = (ProxyGenerator.MethodInfo)me.next();
                    //14.寫入方法
                    proMe.write(outputStream);
                }
                //15.沒有屬性表  直接寫0
                outputStream.writeShort(0);
                //返回一個完整class的字節碼
                return byteOutputStream.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }
    
    
     private void addProxyMethod(Method method, Class<?> class) {
        //獲取到方法名
        String methodName = method.getName();
        //獲取所有方法參數類型
        Class[] methodParamTypes = method.getParameterTypes();
        //獲取返回類型
        Class returnTypes = method.getReturnType();
        //獲取所有的異常
        Class[] exceptions = method.getExceptionTypes();
        //方法名+方法參數類形的方法簽名  用來標識唯一的方法
        String methodSign = methodName + getParameterDescriptors(methodParamTypes);
        //獲取該方法的List<ProxyGenerator.ProxyMethod>
        Object me = (List)this.proxyMethods.get(methodSign);
        if (me != null) {
            //如果不為空 即出現了相同方法簽名的方法
            Iterator iter = ((List)me).iterator();
            //則獲得這個list的迭代器
            while(iter.hasNext()) {
                //開始迭代
                ProxyGenerator.ProxyMethod proxyMe = (ProxyGenerator.ProxyMethod)iter.next();
                //如果這個方法簽名對應的方法描述中返回值也與傳入的一致
                if (returnTypes == proxyMe.returnType) {
                    //則直接合並兩個方法中拋出的所有異常然后直接返回
                    ArrayList methodList = new ArrayList();
                    collectCompatibleTypes(exceptions, proxyMe.exceptionTypes, methodList);
                    collectCompatibleTypes(proxyMe.exceptionTypes, exceptions, methodList);
                    proxyMe.exceptionTypes = new Class[methodList.size()];
                    proxyMe.exceptionTypes = (Class[])methodList.toArray(proxyMe.exceptionTypes);
                    return;
                }
            }
        } 
        //如果為空
        else {
            //則新建一個List
            me = new ArrayList(3);
            //將方法簽名 與這個新建的List<ProxyGenerator.ProxyMethod> 方法描述 put進去
            this.proxyMethods.put(methodSign, me);
        }
        //然后再這個list中新添加一個ProxyMethod 方法描述  這個Proxymethod方法包含了傳入的方法的所有特征
        ((List)me).add(new ProxyGenerator.ProxyMethod(methodName, methodParamTypes, returnTypes, exceptions, class));
    }

 

  這個方法中涉及到兩個重要概念,一個是class字節碼文件構造  參考如下  類型中u后面的數字則代表字節長度   。而上述方法中構造自字節碼文件的15個步驟也是按照如下表一步一步構造的,最后沒有屬性表的值

 

 

 另一個是常量池 ,主要放置兩大類常量,一個是字面量  即常見的字符串,以及聲明為final的變量,另一個則是語言層面的符號引用,包含三類常量:類和接口的全限定名,字段的名稱和描述符,方法的名稱和描述符

生成的字節碼文件如下

package cn.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements IProx {
    
    //方法變量
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    //構造方法
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    //重寫方法
    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    //重寫方法
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //重寫方法
    public final String hello(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    //重寫方法
    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //靜態方法
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("cn.proxy.IProx").getMethod("hello", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 

 

 

上述方法生成代理類字節碼返回后則回到了 第5步中的ProxyClassFactory的apply方法 ,獲取到字節碼文件后 ,生成代理類class並返回,方法為

defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length)   這個方法為本地方法 ,不能獲取到源碼,但也容易理解,就是根據類名,類加載器,字節碼文件創建一個class類型

private static native Class<?> defineClass0(ClassLoader loader, String name,byte[] b, int off, int len);

defineClass0返回了代理類信息后則回到了第4步中的Factory的get()方法中,並將生成的類信息繼續返回到第3步,第3步則繼續返回  一直會返回到第1步

 

 

 

8.所以,回到第1步的代碼中繼續分析

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)throws IllegalArgumentException{
        //InvocationHandler不能為null
        Objects.requireNonNull(h);
    
        final Class<?>[] intfs = interfaces.clone();
        
        //權限校驗
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 查找或者生成代理類  核心邏輯  參數為類加載器和上面的接口數組
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 獲取到代理類的class類  即cl
         */
        try {
            //校驗權限
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //拿到其有參構造方法   即字節碼文件中的 public $Proxy0(InvocationHandler var1)方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            
            //拿到我們傳入的InvocationHandler
            final InvocationHandler ih = h;
            //如果類訪問修飾符不是public  那么則將其設置可訪問
            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);
        }
    }

  獲取到字節碼生成的類信息后,則獲取代理類的有參構造並傳入我們自己一開始定義的InvocationHandler。通過構造函數創建代理類的實例並返回。所以字節碼的父類文件中InvocationHandler則是我們創建的並傳入的,

final class $Proxy0 extends Proxy implements IProx {

    ....
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    ....
    public final String hello(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
    }
}
}
public class Proxy implements java.io.Serializable {
    ....
    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;
    ....
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
}

  由上述代碼可以得知 代理類在執行hello方法時  實際上是通過我們傳入的InvocationHandler的invoke方法進行調用。到此  jdk動態代理的分析以及全部完成

 

結論

至此可以得知  jdk動態代理的大致邏輯即是

傳入代理類 類加載器,與接口數組和自定義的InvocationHandler,然后通過分析接口信息生成java文件的字節碼數據,然后調用本地方法將類加載到內存中,最后返回構造參數為InvocationHandler的代理類,該類實現代理接口,並繼承Proxy類(所以jdk動態代理只能代理接口,java單繼承),我們調用方法實際上是調用代理類的方法,代理類則可以通過我們傳入的InvocationHandler反射調用原本的方法來實現無侵入的修改原有方法邏輯

 

 

 

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM