JDK動態代理實現機制 JDK動態代理實現機制

===========================================html

    原文連接: JDK動態代理實現機制   轉載請註明出處!java

===========================================數據庫

本文只對JDK動態代理的底層實現進行分析,如需瞭解代理模式和動態代理的使用請移步:設計模式—代理模式     動態代理的兩種實現方式(JDK/Cglib)設計模式

在讀代碼以前先清楚兩個概念:api

  一、Class類存儲的是類的全部信息,包括類的全部方法、屬性、實現接口等。每一個類對應一個Class對象(單例),Class對象是由classLoader加載出來的,使用雙親委派模型來保證class只會被加載一次。緩存

  二、classLoader在加載類的時候無論class文件是從哪裏來的,不管是從.class文件、網絡、數據庫類加載器都不關心。他只關心給他的class二進制流是否是可以經過校驗。安全

說明:如下測試代碼和 動態代理的兩種實現方式(JDK/Cglib)相同網絡

使用JDK動態代理須要實現InvocationHandler接口,同時實現invoke()方法。app

package com.zpj.proxy.jdk;

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

/**
 * Created by Perkins on 2017/4/2.
 */
public class JDKProxy implements InvocationHandler {
    private Object person;
    public Object getInstance(Object person) {
        this.person = person;
        return Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("doSomething---------start");
        method.invoke(person, args);
        System.out.println("doSomething---------end");
        return null;
    }
}

測試代碼以下:ide

package com.zpj.proxy.jdk;

/**
 * Created by Perkins on 2017/4/2.
 */
public class Run {

    public static void main(String[] args) {
        Person person = (Person) new JDKProxy().getInstance(new MrLi());
        person.doWork();
    }
}

 

運行的時候在person處打斷點可看到person的類名爲$Proxy0而不是Person或者MrLi,則說明該返回對象是Person的實現類

咱們添加以下代碼把person的class中的方法打印出來

package com.zpj.proxy.jdk;

/**
 * Created by Perkins on 2017/4/2.
 */
import java.lang.reflect.Method;
public class Run {
    private Method method;
    public static void main(String[] args) {
       Person person = (Person) new JDKProxy().getInstance(new MrLi());
       Method [] methods = person.getClass().getMethods();
       for(int i =0; i<methods.length;i++){
           System.out.println(methods[i].getName());
       }
        person.doWork();
    }
} 

 

結果以下,很明顯紅框中的方法不屬於Person也不屬於Object中的方法。這更進一步說明返回的person並非Person的實例。

下面就進入底層代碼對JDK動態代理進行解析。

這裏直接對Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);進行分析。

 

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{
        if (h == null) {
        //驗證InvocationHandler不容許爲null
        throw new NullPointerException();
        }
    /*
     * Look up or generate the designated proxy class.
     */
    //調用getProxyClass()獲取Class實例,該實例即是返回的代理Person的實例,此方法爲重點!!!
        Class cl = getProxyClass(loader, interfaces);
    /*
     * Invoke its constructor with the designated invocation handler.
     */
        try {
        //利用反射機制從Class中取出構造器建立對象
        Constructor cons = cl.getConstructor(constructorParams);
        return (Object) cons.newInstance(new Object[] { h });
        } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
        } catch (IllegalAccessException e) {
        throw new InternalError(e.toString());
        } catch (InstantiationException e) {
        throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
        throw new InternalError(e.toString());
        }
        }

 

 在上面方法中調用public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)throws IllegalArgumentException獲取了Class實例,下面進入該方法進行分析。

public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException {
        if (interfaces.length > 65535) {
            //驗證接口數量不容許超過65535
            throw new IllegalArgumentException("interface limit exceeded");
        }
        //************開始對interface進行循環驗證,驗證經過則加入interfaceNames中***************************
        Class proxyClass = null;
    /* collect interface names to use as key for proxy class cache */
        String[] interfaceNames = new String[interfaces.length];
        Set interfaceSet = new HashSet();    // for detecting duplicates
        for (int i = 0; i < interfaces.length; i++) {//循環對全部接口進行操做
        /*
         * Verify that the class loader resolves the name of this
         * interface to the same Class object.
         */
            String interfaceName = interfaces[i].getName();
            Class interfaceClass = null;
            try {
                //根據名稱獲取接口的Class
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(interfaces[i] + " 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.contains(interfaceClass)) {
                throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);
            interfaceNames[i] = interfaceName;
        }
//************結束對interface進行循環驗證,存儲於interfaceNames中***************************
    /*
     * Using string representations of the proxy interfaces as
     * keys in the proxy class cache (instead of their Class
     * objects) is sufficient because we require the proxy
     * interfaces to be resolvable by name through the supplied
     * class loader, and it has the advantage that using a string
     * representation of a class makes for an implicit weak
     * reference to the class.
     */
        Object key = Arrays.asList(interfaceNames);

    /*
     * Find or create the proxy class cache for the class loader.
     */
        Map cache;
        synchronized (loaderToCache) {
            cache = (Map) loaderToCache.get(loader);
            if (cache == null) {
                cache = new HashMap();
                loaderToCache.put(loader, cache);
            }
        /*
         * This mapping will remain valid for the duration of this
         * method, without further synchronization, because the mapping
         * will only be removed if the class loader becomes unreachable.
         */
        }

    /*
     * Look up the list of interfaces in the proxy class cache using
     * the key.  This lookup will result in one of three possible
     * kinds of values:
     *     null, if there is currently no proxy class for the list of
     *         interfaces in the class loader,
     *     the pendingGenerationMarker object, if a proxy class for the
     *         list of interfaces is currently being generated,
     *     or a weak reference to a Class object, if a proxy class for
     *         the list of interfaces has already been generated.
     */
        synchronized (cache) {
        /*
         * Note that we need not worry about reaping the cache for
         * entries with cleared weak references because if a proxy class
         * has been garbage collected, its class loader will have been
         * garbage collected as well, so the entire cache will be reaped
         * from the loaderToCache map.
         */
            do {
                Object value = cache.get(key);
                if (value instanceof Reference) {
                    proxyClass = (Class) ((Reference) value).get();
                }
                if (proxyClass != null) {
                    // proxy class already generated: return it
                    return proxyClass;
                } else if (value == pendingGenerationMarker) {
                    // proxy class being generated: wait for it
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
            /*
             * The class generation that we are waiting for should
             * take a small, bounded time, so we can safely ignore
             * thread interrupts here.
             */
                    }
                    continue;
                } else {
            /*
             * No proxy class for this list of interfaces has been
             * generated or is being generated, so we will go and
             * generate it now.  Mark it as pending generation.
             */
                    cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }
        try {
            String proxyPkg = null;    // package to define proxy class in
        /*
         * 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.
         */
        //尋找到package的包路徑,爲構建代理類作準備。同時要保證全部的非public代理接口在相同的包中
            for (int i = 0; i < interfaces.length; i++) {
                int flags = interfaces[i].getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = interfaces[i].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,
                proxyPkg = "";        // use the unnamed package
            }
            {
        /*
         * Choose a name for the proxy class to generate.
         */
                long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                //這裏構建了Class的全類名:包名+$Proxy+num,這裏的proxyClassNamePrefix=「$Proxy」
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
        /*
         * Verify that the class loader hasn't already
         * defined a class with the chosen name.
         */

        /*
         * Generate the specified proxy class.
         */
                //該方法爲核心方法,獲取代理類的字節碼數據流。也便是proxyName.class文件的數據流
                //由於interface的全部class文件都不已經被加載,因此這裏只須要根據名稱就能夠從JVM中讀取出全部的二進制數據
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
                try {
                    //調用native方法從class字節碼文件中建立代理類的Class實例。這裏再也不進入分析,由JVM負責實現
                    //獲取到代理類的Class實例後,程序就能夠根據代理類Class經過反射進行對象建立
                    proxyClass = 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());
                }
            }
            // add to set of all generated proxy classes, for isProxyClass
            proxyClasses.put(proxyClass, null);
        } finally {
        /*
         * We must clean up the "pending generation" state of the proxy
         * class cache entry somehow.  If a proxy class was successfully
         * generated, store it in the cache (with a weak reference);
         * otherwise, remove the reserved entry.  In all cases, notify
         * all waiters on reserved entries in this cache.
         */
            synchronized (cache) {
                if (proxyClass != null) {
                    cache.put(key, new WeakReference(proxyClass));
                } else {
                    cache.remove(key);
                }
                cache.notifyAll();
            }
        }
        //返回代理類的Class實例
        return proxyClass;
    }
}

 在上面的方法中,核心就在於如何構建代理類的class字節碼文件。由於該字節碼文件是由代理類和目標類組合而成,這也便是在運行期間動態建立class的方法。

 即:byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);該方法的具體實現,看下面代碼分析

 

 public static byte[] generateProxyClass(final String proxyName, Class[] interfaces) {
        //建立代理生成器
        ProxyGenerator proxyGenerator = new ProxyGenerator(proxyName, interfaces);
        //調用generateClassFile()生成class的二進制數據流
        final byte[] classFile = proxyGenerator.generateClassFile();
        //該參數能夠在運行時配置,當爲true時則程序會生成代理類的class文件保存在磁盤中,即: $Proxy0.class
        if(saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    try {
                        FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(proxyName) + ".class");
                        var1.write(classFile);
                        var1.close();
                        return null;
                    } catch (IOException var2) {
                        throw new InternalError("I/O exception saving generated file: " + interfaces);
                    }
                }
            });
        }
        return classFile;
    }

 

 繼續進入proxyGenerator.generateClassFile()分析

 private byte[] generateClassFile() {
        //注意這三個方法,hashCodeMethod、equalsMethod、toStringMethod
        //這三個方法來自於Object,代理類一樣須要Object的這三個方法
        //把這三個方法名稱和與之匹配的Object,class進行緩存
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);

        int var1;
        int var3;
        //這裏循環對目標類所實現的全部接口中的方法進行緩存
        for(var1 = 0; var1 < this.interfaces.length; ++var1) {
            //獲取接口中的方法
            Method[] var2 = this.interfaces[var1].getMethods();
            for(var3 = 0; var3 < var2.length; ++var3) {
                this.addProxyMethod(var2[var3], this.interfaces[var1]);
            }
        }
        Iterator var7 = this.proxyMethods.values().iterator();
        List var8;
        //把代理類須要建立的方法緩存在var8中。這裏說明一下,由於該源碼是從class中讀取出來的,因此變量名在進行編譯的時候被更改了,這裏閱讀的時候須要注意一些
        while(var7.hasNext()) {
            var8 = (List)var7.next();
            checkReturnTypes(var8);
        }
        Iterator var11;
        try {
            //添加構造器方法,至此代理類中所須要添加的方法添加完成
            this.methods.add(this.generateConstructor());
            var7 = this.proxyMethods.values().iterator();
            //循環把須要的變量和方法添加在fileds和methods中緩存
            while(var7.hasNext()) {
                var8 = (List)var7.next();
                var11 = var8.iterator();
                while(var11.hasNext()) {
                    ProxyGenerator.ProxyMethod var4 = (ProxyGenerator.ProxyMethod)var11.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(var4.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    this.methods.add(var4.generateMethod());
                }
            }
            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var6) {
            throw new InternalError("unexpected I/O Exception");
        }
        //對方法和參數進行安全性驗證
        if(this.methods.size() > '\uffff') {
            throw new IllegalArgumentException("method limit exceeded");
        } else if(this.fields.size() > '\uffff') {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            for(var1 = 0; var1 < this.interfaces.length; ++var1) {
                this.cp.getClass(dotToSlash(this.interfaces[var1].getName()));
            }
            this.cp.setReadOnly();
            //構建緩衝流存放動態生成的字節碼文件數據
            ByteArrayOutputStream var9 = new ByteArrayOutputStream();
            DataOutputStream var10 = new DataOutputStream(var9);
            try {
                //這裏就是按照class文件格式進行封裝,這裏再也不詳解
                var10.writeInt(-889275714);
                var10.writeShort(0);
                var10.writeShort(49);
                this.cp.write(var10);
                var10.writeShort(49);
                var10.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var10.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                var10.writeShort(this.interfaces.length);
                for(var3 = 0; var3 < this.interfaces.length; ++var3) {
                    //添加全部方法的字節碼數據
                    var10.writeShort(this.cp.getClass(dotToSlash(this.interfaces[var3].getName())));
                }
                var10.writeShort(this.fields.size());
                var11 = this.fields.iterator();
                while(var11.hasNext()) {
                    //添加全部變量的字節碼數據
                    ProxyGenerator.FieldInfo var12 = (ProxyGenerator.FieldInfo)var11.next();
                    var12.write(var10);
                }
                var10.writeShort(this.methods.size());
                var11 = this.methods.iterator();
                while(var11.hasNext()) {
                    ProxyGenerator.MethodInfo var13 = (ProxyGenerator.MethodInfo)var11.next();
                    var13.write(var10);
                }
                var10.writeShort(0);
                //動態組合class二進制字節碼結束,進行返回。這裏存儲的就是一個完整class文件數據。調用處據今生成Class對象實例
                return var9.toByteArray();
            } catch (IOException var5) {
                throw new InternalError("unexpected I/O Exception");
            }
        }
    }

 

 至此,JDK動態代理的實現結束。由以上可見,其核心就是動態的生成代理類的class字節碼數據,而後調用native方法從字節碼數據中建立代理對象的Class實例,拿到Class實例後經過Java的反射技術生成代理類

 那麼該代理類的結果到底如何?咱們能夠在main方法中添加 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");配置,這樣在程序運行的時候會把代理類的字節碼文件保存在類路徑下。經過反編譯能夠讀取到代理類的具體詳情。

下面看反編譯後的代理類文件

package com.sun.proxy;

import com.zpj.proxy.jdk.Person;

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

//注意這裏$Proxy0 extends Proxy implements Person,這裏也便是目標類必須實現的有接口的緣由
/**
 * 代理類名稱:$Proxy0
 * 繼承:Proxy
 * 實現:Person
 */
public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler paramInvocationHandler) throws {
        super(paramInvocationHandler);
    }

    //繼承自Object的方法
    public final boolean equals(Object paramObject) throws {
        try {
            return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }
    //繼承自Object的方法
    public final String toString()throws {
        try {
            return (String) this.h.invoke(this, m2, null);
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }
    //繼承自Object的方法
    public final int hashCode()throws {
        try {
            return ((Integer) this.h.invoke(this, m0, null)).intValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }
    //實現Person的方法
    public final void doWork()throws {
        try {
            //由此處能夠看出目標方法的調用循序
            /**
             * 代理類先調用實現接口的方法,在該方法中調用InvocationHandler的invoke方法。
             * 而在invoke中由經過注入進去的methods經過反射調用目標類的目標方法doWork()
             */
            this.h.invoke(this, m3, null);
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.zpj.proxy.jdk.Person").getMethod("doWork", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }
}

 

 

------------end

相關文章
相關標籤/搜索