拆輪子系列--java動態代理源碼分析

0.前言

代理模式是常見的設計模式,通常分爲普通代理模式和動態代理模式,區別就在於代理類一個是本身寫的,一個是動態生成的。不管是靜態代理模式仍是動態代理模式,其實結構都是相同的,看看UML類圖。java

(UML類圖在編碼設計的時候有很大做用,有時間寫篇文章) 經過UML類圖能夠很清晰的看到代理類Proxy和被代理類RealSubject都實現了Subject接口,同時Proxy持有了RealSubject對象。經過代理類,咱們就能夠在被代理類的方法執行前和執行後執行一些咱們須要的東西。動態代理就是這個Proxy是動態生成的。編程

1.動態代理用法

//Demo
import java.util.*;
import java.lang.reflect.*;

public class ProxyTest
{

   public static void main(String[] args)
   {

   	    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Bat bat=new Bat();
        FlyableInvocationHandler handler=new FlyableInvocationHandler(bat);
        Flyable flyable= (Flyable) Proxy.newProxyInstance(Flyable.class.getClassLoader(),new Class<?>[]{Flyable.class},handler);
        flyable.fly();
   }

}

interface Flyable
{
    void fly();
}

class Bat implements Flyable
{

    @Override
    public void fly()
    {
        System.out.println("fly in dark night");
    }
}

class FlyableInvocationHandler implements InvocationHandler
{
    private Flyable flyable;

    public FlyableInvocationHandler(Flyable flyable)
    {
        this.flyable = flyable;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        System.out.println("展翅");
        Object result = method.invoke(flyable, args);
        System.out.println("收翅");
        return result;
    }
}

複製代碼

這是一個動態代理的基本用法,經過調用Proxy.newProxyInstance生成一個代理類,這個代理類如何生成的,接着就看一下Proxy.newProxyInstance這個方法設計模式

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        // Android-changed: sm is always null
        // 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.
         */
        try {
            // Android-changed: sm is always null
            // if (sm != null) {
            //     checkNewProxyPermission(Reflection.getCallerClass(), cl);
            // }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                // Android-changed: Removed AccessController.doPrivileged
                cons.setAccessible(true);
            }
            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對象數組

/*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);
複製代碼

關於這個Class對象是如何生的,咱們待會兒再說。接着往下看。緩存

final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                // Android-changed: Removed AccessController.doPrivileged
                cons.setAccessible(true);
            }
            return cons.newInstance(new Object[]{h});
複製代碼

經過代理類的class對象,找到其中參數類型爲constructorParams的構造函數,這個constructorParams又是是什麼bash

private static final Class<?>[] constructorParams =
        { InvocationHandler.class };
複製代碼

能夠看到這是一個只包含InvocationHandler.class的Class數組。到這裏咱們就能夠推測出,生成的代理類的某個構造函數是要傳入InvocationHandler對象的,而這個InvocationHandler對象就是咱們在調用Proxy.newInstance()方法時傳入的,對應Demo裏的FlyableInvocationHandler。以後就是調用該構造函數傳入InvocationHandler參數構造一個對象並返回。app

2.代理類的生成

上面說了,代理類的Class對象是由ide

Class<?> cl = getProxyClass0(loader, intfs);
複製代碼

生成的。那麼接着看看這個getProxyClass0方法函數

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        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);
    }
複製代碼

首先是一個65535的判斷,若是接口數組的大小超過65535,就會報錯,至於爲何是65535,不太清楚, 接着就是調用這個方法proxyClassCache.get(loader, interfaces)返回class對象,這個proxyClassCache是Proxy的一個成員變量ui

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

這裏你能夠理解爲代理類的緩存,咱們重點關注的就是ProxyClassFactory這個類,從名字也能夠看出,這就是咱們要找的生成代理類Class對象的地方。

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

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use the default package.
                proxyPkg = "";
            }

            {
                // Android-changed: Generate the proxy directly instead of calling
                // through to ProxyGenerator.
                List<Method> methods = getMethods(interfaces);
                Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                validateReturnTypes(methods);
                List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);

                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);

                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                return generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
            }
        }
    }
複製代碼

接下來分析一下這個方法

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

複製代碼

這段代碼就是用來生驗證類加載器是否解析了此名稱接口到同一個Class對象。其中有一行代碼

if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
複製代碼

判斷Class對象是不是接口類型,這就要求咱們調用Proxy.newInstance方法時傳入的Class<?>[] interfaces是接口的class對象。 剩下的代碼使用來得到代理類的包名,代理類的名稱以及代理類的Method數組,就再也不細說了。最終調用

generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
複製代碼

生成代理類的Class對象,這個方法是一個native方法,就不進去再看了,分析到這裏也差很少了。

3.代理類字節碼

想要輸出動態代理生成的字節碼文件默認是不輸出的,想輸出到存儲空間調用

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
複製代碼

反編譯字節碼

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 Flyable
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject)
  {
    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 void fly()
  {
    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()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    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") });
      m3 = Class.forName("Flyable").getMethod("fly", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
    }
  }
}

複製代碼

和咱們設想的同樣,代理類持有了InvocationHandler對象,同時實現了傳入的Interface接口,能夠看到fly方法裏調用了InvocationHandler的invoke方法,傳入了this指針,方法method和參數null。

4.應用

至此動態代理基本上就分析完了,動態代理最多見的用處可能就是AOP編程了,在不改變已有類的狀況下生成代理添加新的邏輯,有興趣的能夠看一下AOP編程。

關注個人公衆號

相關文章
相關標籤/搜索