Java動態(tài)代理機制分析

前言

Java動態(tài)代理通過反射的機制實現(xiàn)在運行時,基于傳入的指定一組接口及委托類對象,動態(tài)的產(chǎn)生代理類,代理類負責將所有的方法調(diào)用分派到委托對象上反射執(zhí)行。動態(tài)代理類的創(chuàng)建十分簡單:

InvocationHandler handler = new MyInvocationHandler(...);
     Class proxyClass = Proxy.getProxyClass(
         Foo.class.getClassLoader(), new Class[] { Foo.class });
     Foo f = (Foo) proxyClass.
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });

或者

 Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class[] { Foo.class },
                                          handler);

有動態(tài)代理,那一定有靜態(tài)代理。所以我們先從靜態(tài)代理看起,一步步分析Java的動態(tài)代理機制。

靜態(tài)代理

說到靜態(tài)代理,其實就是我們手工對代理模式的實現(xiàn)。代理模式是什么呢?

代理是一種常用的設(shè)計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問。代理類負責為委托類預(yù)處理消息,過濾消息并轉(zhuǎn)發(fā)消息,以及進行消息被委托類執(zhí)行后的后續(xù)處理。

代理模式的UML圖如下所示:


Markdown

為了保持行為的一致性,代理類和委托類通常會實現(xiàn)相同的接口,所以在訪問者看來兩者沒有絲毫的區(qū)別。通過代理類這中間一層,能有效控制對委托類對象的直接訪問,也可以很好地隱藏和保護委托類對象,同時也為實施不同控制策略預(yù)留了空間,從而在設(shè)計上獲得了更大的靈活性。

我們看一個簡單的代理模式的例子:

public interface Bird {
    public void fly();
}
public class RealBird implements Bird {
    private String name;

    public RealBird(String name) {
        this.name = name;
    }

    public void fly() {
        System.out.println(String.format("%s is flying...", name));
    }
}

public class ProxyBird implements Bird{
    private String name;
    
    private Bird bird;
    
    public ProxyBird(String name) {
        this.name = name;
    }

    public void fly() {
        if (null == bird){
            this.bird = new RealBird(name);
        }
        bird.fly();
    }
}
public class ProxyExample {
    public static void main(String[] args){
        Bird eagle = new RealBird("eagle");
        Bird swan = new ProxyBird("swan");
        eagle.fly();
        swan.fly();
    }
}
eagle is flying...
swan is flying...

代理模式使得代理類控制了客戶端對委托類的訪問,這使得我們可以在代理類中做一些其他的操作,如日志記錄、統(tǒng)計、緩存等,這些都是對用戶隱藏的。同時代理類實現(xiàn)與委托類相同接口,代理模式的使用對用戶來說是透明的。
Java動態(tài)代理使得我們不必手寫大量代理類,通過反射方式,在使用時動態(tài)的生成代理類。

動態(tài)代理

相關(guān)類與接口

動態(tài)代理的核心就在于java.lang.reflect.Proxy類與java.lang.reflect.InvocationHandler這個接口。

  • Proxy類:它是Java動態(tài)代理的生成器,它提供了以下靜態(tài)方法來生成動態(tài)代理類。
public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces);//根據(jù)傳入的類加載器、接口生成代理類對象
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h);//根據(jù)傳入的類加載器、接口、調(diào)用處理器動態(tài)生成代理類
public static InvocationHandler getInvocationHandler(Object proxy);//獲取代理類對應(yīng)的調(diào)用處理器
  • InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args);//調(diào)用處理器接口,每個動態(tài)類必須關(guān)聯(lián)一個InvocationHandler,通過反射在代理類上執(zhí)行傳入的方法。

源碼分析

我們通過一個簡單的動態(tài)代理的例子來具體分析動態(tài)代理機制。

public interface Subject {
    public void doSomething();
}
public class RealSubject implements Subject{

    public void doSomething() {
        System.out.println("do something...");
    }
}
public class ProxyInvocationHandler implements InvocationHandler{
    private Object proxies;
    
    public ProxyInvocationHandler(Object proxies){
        this.proxies = proxies;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        //此處可以做一些方法調(diào)用的前置操作
        PrintUtil.print(String.format("proxy:%s,method:%s,args:%s", proxies,method,args));
        result = method.invoke(proxies, args);
        //此處可以做一些方法調(diào)用的后置操作
        return result;
    }
}
public class ProxyTest {
    public static void main(String[] args){
        Subject realSubject = new RealSubject();//實際對象
        Subject subject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, new ProxyInvocationHandler(realSubject));
        subject.doSomething();
    }
}
do something...

從上面的簡單例子我們可以總結(jié)出使用Java的動態(tài)代理的幾個步驟:

我們從Proxy類的靜態(tài)方法newProxyInstance開始追蹤起,源碼如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException{
        if (h == null) {
            throw new NullPointerException();
        }

        final Class<?>[] intfs = interfaces.clone();//復(fù)制interfaces接口數(shù)組
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);//接口權(quán)限檢查
        }

        /*
         * Look up or generate the designated proxy class.
         * 查找或生成代理類
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);//獲取代理類的構(gòu)造器對象
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);//實例化一個代理類的實例
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

/**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {//該代理類代理的委托類的接口不能超過65536個
            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
        //從緩存中獲取 由對應(yīng)的ClassLoader加載的實現(xiàn)了對應(yīng)接口的代理類,如果沒有緩存,則先創(chuàng)建一個代理類,然后緩存起來。
        return proxyClassCache.get(loader, interfaces);//
    }

/**
     * a cache of proxy classes
     * proxyClassCache 是Proxy實例的私有變量,是一個由WeakCache構(gòu)成的緩存結(jié)構(gòu)
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

WeakCache的get操作源碼如下:

    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        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
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        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)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                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來完成的。生成代理類的核心邏輯是在apply方法中,我們繼續(xù)分析:

/**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names 所有代理類都有一個$Proxy前綴
        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 {
                    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

            /*
             * 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();
                if (!Modifier.isPublic(flags)) {
                    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 + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            //前面的邏輯都是在拼接一個代理類的全限定名
            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces);//此處生成代理類字節(jié)碼文件
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);//類加載器把該類的字節(jié)碼加載進內(nèi)存升策劃那個該類的class對象
            } 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());
            }
        }
    }

最后是通過JDK自帶的字節(jié)碼生成工具類ProxyGenerator,根據(jù)傳入的代理類名以及代理類需要實現(xiàn)的接口來完成代理類字節(jié)碼文件的生成:

public static byte[] generateProxyClass(String paramString, Class[] paramArrayOfClass) {
        ProxyGenerator localProxyGenerator = new ProxyGenerator(paramString, paramArrayOfClass);
        byte[] arrayOfByte = localProxyGenerator.generateClassFile();
        if (saveGeneratedFiles)
            AccessController.doPrivileged(new PrivilegedAction(paramString, arrayOfByte) {
                public Void run() {
                    try {
                        FileOutputStream localFileOutputStream = new FileOutputStream(
                                ProxyGenerator.access$000(this.val$name) + ".class");
                        localFileOutputStream.write(this.val$classFile);
                        localFileOutputStream.close();
                        return null;
                    } catch (IOException localIOException) {
                        throw new InternalError("I/O exception saving generated file: " + localIOException);
                    }
                }
            });
        return arrayOfByte;
    }

generateProxyClass方法生成代理類的核心在于generateClassFile方法,該方法的主要工作就是根據(jù)JAVA字節(jié)碼規(guī)范,根據(jù)我們設(shè)定的目的-生成一個帶有一些接口的代理類,最終在內(nèi)存中生成該代理類的字節(jié)碼流,然后寫入磁盤,最終該代理類就生成了。然后類加載器加載該字節(jié)碼,我們就可以在程序中使用該代理類了。
注:我們在generateProxyClass方法中可以看到一點,當saveGeneratedFiles為true時,我們才會執(zhí)行后面的把生成的代理類寫到磁盤存儲下來。但saveGeneratedFiles的值是在哪里設(shè)置的呢?查找ProxyGenerator類,發(fā)現(xiàn):

private static final boolean saveGeneratedFiles;
static {
        saveGeneratedFiles = ((Boolean) AccessController
                .doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
        try {
            hashCodeMethod = Object.class.getMethod("hashCode", new Class[0]);
            equalsMethod = Object.class.getMethod("equals", new Class[] { Object.class });
            toStringMethod = Object.class.getMethod("toString", new Class[0]);
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
    }

ProxyGenerator類初始化的時候,會去系統(tǒng)屬性里查找‘sun.misc.ProxyGenerator.saveGeneratedFiles’屬性的val并賦值給saveGeneratedFiles。所以很清楚了,我們只需要在系統(tǒng)Property里設(shè)置 ‘sun.misc.ProxyGenerator.saveGeneratedFiles’屬性為true,我們使用的動態(tài)代理類就會落地到磁盤上了。
我們運行一下該代理類:

public class ProxyTest {    
    public static void main(String[] args){
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Subject sub = new RealSubject();
        Subject proxy = (Subject)Proxy.newProxyInstance(sub.getClass().getClassLoader(), sub.getClass().getInterfaces(), new ProxyInvocationHandler(sub));
        proxy.doSomething();
    }
}

報錯:

Exception in thread "main" java.lang.InternalError: I/O exception saving generated file: java.io.FileNotFoundException: com\sun\proxy$Proxy0.class (系統(tǒng)找不到指定的路徑。)

手動創(chuàng)建目錄,得到落地的動態(tài)代理類$Proxy0字節(jié)碼文件,反編譯得到:

public final class $Proxy0 extends Proxy
  implements Subject
{
  private static Method m1;
  private static Method m0;
  private static Method m3;
  private static Method m2;

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

  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);
    }
  }

  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);
    }
  }

  public final void doSomething()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  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);
    }
  }

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

總結(jié)

以上就是我們對Java動態(tài)代理機制的分析,通過源碼分析,我們知道了jdk是通過代理類生成器生成了代理類。通過對全局屬性‘sun.misc.ProxyGenerator.saveGeneratedFiles’的設(shè)置,該動態(tài)類可以落地到磁盤。

參考

  • jdk1.7源碼
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,623評論 18 399
  • 背景:學(xué)習spring的AOP或者EasyMock的源碼時,需要對java的動態(tài)代理有深刻的了解 關(guān)于cglib的...
    測試你個頭閱讀 830評論 0 1
  • 最近跟著老師學(xué)習了做一個簡單的QQ郵箱的首頁界面,每個星期堅持打卡,希望學(xué)有所成。 首先是將界面分成2個部分:he...
    mjlwt閱讀 2,312評論 3 3

友情鏈接更多精彩內(nèi)容