Java動態代理細探

##簡介 代理模式 使用ProxyGenerator類生成字節碼java

前面的2篇文章都提到一些Java動態代理的東西,接下來咱們就更加細緻的來聊一聊Java的動態代理。 Java對於動態代理主要提供了Proxy類,在Proxy類中有一個工廠方法newProxyInstance這基本上就是Java Proxy動態代理的核心了。首先看一下這個方法的前面。編程

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

Proxy的這個靜態工廠的目的就是生成一個代理類的實例,第1個參數loader指明動態生成的類由哪個類加載器來加載。第2個interfaces表示要代理哪些接口的中的方法。注意:Java動態代理中的最終代理的都是方法。後面會從動態生成的類來講明這個問題。第3個參數h是一個InvocationHandler接口。緩存

首先,思考一下咱們爲何要使用接口?這裏先記住一點,很重要的一點,咱們使用接口的重要的一個方面是對變化業務的抽象,咱們知道這個位置有一些重要的事情要作,可是具體怎麼作是變化的,須要使用這個接口(或者方法)的客戶本身決定(實現)。可是如今客戶尚未實現,這個類都沒有,沒有辦法處理依賴關係,怎麼辦?固然是抽象一個接口,依賴於接口就能夠了,客戶使用的時候經過面向對象的多態機制,實現接口回調。這就是面向對象很是強調的一點面向接口編程。這裏的newProxyInstance所依賴的就是抽象類ClassLoader和接口InvocationHandler。多線程

如今咱們在來看InvocationHandler抽象了什麼,InvocationHandler接口中只有一個方法app

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

前面咱們說了Java中的動態代理都是針對於方法的代理,invoke方法抽象的就是代理方法的處理過程。代理模式本質上的東西就是針對一個方法,使用一個代理方法,在代理方法中除了處理被代理的方法,順便處理一下其餘的事情,這也是使用代理模式的目的。invoke就是抽象的這個過程,因此實現InvocationHandler接口要完成的事情就是處理一個邏輯,代理方法要完成一些什麼事情,這些由客戶實現就能夠了。ide

newProxyInstance 生成代理實例的靜態工廠方法

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

        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
        }

        Class<?> cl = getProxyClass0(loader, interfaces);
      
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                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());
        }
    }

newProxyInstance方法主要就是先生成代理的類的Class,而後獲取構造函數把客戶端傳過來的InvacationHandler注入進去。生成代理類的方法詳細的介紹間下面的getProxyClass0方法。後面會細緻的經過介紹一下生成的代理類的結構來講明爲何必定要使用要求注入InvacationHandler實例。函數

getProxyClass0 動態生成加載代理類Class

getProxyClass0這個方法主要的做用是動態生成代理類的字節碼,獲取字節碼的Class。其實這邊類之因此比較複雜是由於考慮了緩存和同步的工做,這也是這個方法的職責。由於字節碼生成是經過ProxyGenerator.generateProxyClass生成的,文章使用ProxyGenerator類生成字節碼有介紹。後面再介紹一下一些細節問題。而獲取Class也是經過本地方法defineClass0獲取的。 這裏之因此介紹這個類是爲了幫助理解Proxy動態代理的一些細節、限制以及瞭解這個方法中一些多線程同步的一些技巧。學習

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        //代理的接口最多65535個,這和字節碼自己的設計有關
        //本身寫的類直接實現接口數也不能超過65535個
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        Class<?> proxyClass = null;

        /* 收集全部接口的名字用作生成代理類緩存的key */
        String[] interfaceNames = new String[interfaces.length];

        // 去除重複的接口
        Set<Class<?>> interfaceSet = new HashSet<>();

        //檢查每個Class是否是接口,能夠不能夠被指定的loader加載
        //顯然,若是接口不能被loader加載,生成的代理類也沒有辦法加載
        for (int i = 0; i < interfaces.length; i++) {
            //獲取全限定名
            String interfaceName = interfaces[i].getName();
            Class<?> interfaceClass = null;
            try {
                //檢查指定的類加載器loader可否加載指定接口
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(
                    interfaces[i] + " is not visible from class loader");
            }

            //檢查Class是不是一個interface
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }

            //檢查有沒有重複的接口
            if (interfaceSet.contains(interfaceClass)) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);
            //收集接口的全限定名
            interfaceNames[i] = interfaceName;
        }

        //全部接口的名字做爲cache的key
        List<String> key = Arrays.asList(interfaceNames);

        //cache 是全部接口名字與生成的代理類的映射
        Map<List<String>, Object> cache;

        //laderToCache 是一個WeakhashMap這裏檢查loader對應的cache有沒有過時
        synchronized (loaderToCache) {
            cache = loaderToCache.get(loader);
            if (cache == null) {
                cache = new HashMap<>();
                loaderToCache.put(loader, cache);
            }
        }

        
        synchronized (cache) {
           
            do {
                Object value = cache.get(key);
                //WeakReference instanceof Reference //true
                //null instanceof Reference  //false
                //cache中放了Reference類型和pendingGenerationMarker(Object)
                if (value instanceof Reference) {
                    proxyClass = (Class<?>) ((Reference) value).get();
                }
                //代理類已經被生成,而且沒有被回收
                if (proxyClass != null) {
                    return proxyClass;
                } 
                //代理類正在被其餘線程生成
                else if (value == pendingGenerationMarker) {
                    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 {
                    //代理類尚未被生成,先標記一個生成標誌
                    //表示這個代理類由我這個線程生成了,其餘線程就不用生成了
                    cache.put(key, pendingGenerationMarker);
                    //結束循環,去後面執行生成代理類的代碼
                    //而且釋放cache的鎖
                    break;
                }
            } while (true);
        }

        //下面這一塊是檢查把生成的代理類放到哪個包中
        //若是有接口不是public的就應該把生成的代理類放到不是public
        //的哪個包中,否則代理類沒有訪問權限
        //若是有不是public的接口再2個以上的不一樣的包,顯然是不合法的
        //若是都是public的接口,則使用默認的包"com.sun.proxy"
        try {
            String proxyPkg = null;

            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) {
                // 都是public接口使用 com.sun.proxy 包
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            {
                //生成代理類的名字,像是$Proxy0,$Proxy1,$Proxy2...
                long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                //生成字節碼的類和方法,後面詳細介紹生成的類的格式
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces);
                try {
                    //把字節碼轉換爲Class
                    proxyClass = defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    throw new IllegalArgumentException(e.toString());
                }
            }
           // private static Map<Class<?>, Void> proxyClasses =
        //Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());
        //緩存生成的代理,給isProxyClass方法使用,這裏proxyClasses之因此要使用Map,
//是爲了直接使用WeakHashMap,可讓生成的代理類能夠在合適的時機被回收
            proxyClasses.put(proxyClass, null);

        } finally {
            synchronized (cache) {
                //若是生成成功了,就把生成的代理類的弱引用緩存起來
                if (proxyClass != null) {
                    cache.put(key, new WeakReference<Class<?>>(proxyClass));
                } 
                //若是沒有生成成功,移除生成中的標誌pendingGenerationMarker
                //讓代理類有從新生成的機會
                else {
                    cache.remove(key);
                }
                //通知其餘等待生成代理類的線程
                cache.notifyAll();
            }
        }
        return proxyClass;
    }

動態生成的代理類的結構

public final class $Proxy extends Proxy
  implements UserMapper
{
  private static Method m3;

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

  public final int insert(User paramUser)
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m3, new Object[] { paramUser })).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m3 = Class.forName("cn.freemethod.dao.mapper.ms.UserMapper").getMethod("insert", 
new Class[] { Class.forName("cn.freemethod.to.User") });
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

上面這個類是經過this

ProxyGenerator.generateProxyClass(
                    proxyName, interfaces)

生成的,這裏爲了說明它的結構,只截取了一小部分,更多的內容請參考 使用ProxyGenerator類生成字節碼 咱們能夠看到生成的類是繼承了Proxy,而且實現了UserMapper接口,UserMapper就是方法.net

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

的interfaces參數傳遞進來的接口,這裏只有一個接口,因此只實現了這一個接口。生成的代理類有一個帶參數InvocationHandler的構造函數,這個是Proxy類須要的。 生成的代理類爲代理的接口中的每個方法生成一個靜態方法,經過Class.forName實現的,在類初始化的時候就完成了。 既然實現了接口確定是要實現接口中的方法的,咱們看生成的insert方法

return ((Integer)this.h.invoke(this, m3, new Object[] { paramUser })).intValue();

就是回調的客戶端傳進來的InvocationHandler實例h的invoke方法。這樣咱們就把代理類的實例,代理的方法和參數都傳遞進去了。在invoke方法中最多見的使用Method方式是: method.invoke(target, args);target是被代理的實例,這樣調用的就是被代理實例上的method方法。

總結

首先,使用代理的目的。就是作一件事情的時候順便作一些其餘的事情,你可能會想我直接在方法中作就能夠了嘛,爲何非要在代理方法中作呢?這主要考慮到一下公共的方法或者邏輯。好比記錄一個方法的執行時間,你固然能夠在須要記錄時間的方法中寫

long start = System.currentTimeMillis();
//doSomething
long end = System.currentTimeMillis();
log.info("method name time cost:"+(end-start));

這樣的邏輯,可是重複的代碼絕對是代碼的"壞味道",也增長了工做量,這樣若是邏輯比較複雜的話,你修改一處,就會修改n處,僅僅是找這n處代碼就是一件痛苦的事情,對吧?更加恐怖的是已經設計好的結構,添加新的公共的邏輯,動態代理絕對是一件利器。 咱們仍是以這個簡單的記錄時間的邏輯爲例,來講明Proxy代理的流程。首先要代理確定是要代理實例的對吧?這個生成代理實例這種比較複雜的事情Proxy已經幫咱們實現了,提供了一個

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

這樣的靜態工廠方法,方法是提供了,可是參數的咱們本身來,第1個參數loader用來加載生成的代理類,爲何非要loader呢?這是但願客戶端保證這個loader能加載第2個參數指定的接口,這樣纔可以加載實現了這些接口的代理類。第2個參數要代理那些接口(方法),用記錄方法執行時間的邏輯就表示,要執行那些方法的執行時間。第3個參數InvocationHandler就是表示公共邏輯,因此得明白要處理什麼邏輯,如今是要記錄方法的執行時間。這個就好辦了,直接實現這個接口重寫這個接口的invoke方法就能夠了,處理一些相似於下面的邏輯就能夠了:

public class TimeCostProxy implements InvocationHandler {
 
    //被代理的對象
    private Object target;
   
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
     {
        long start = System.currentTimeMillis();
        Object result = method.invoke(target,args);
        long end = System.currentTimeMillis();
        log.info("method name time cost:"+(end-start));
        return result;
    }
}

newProxyInstance方法返回的代理對象實現了interfaces,因此徹底能夠把newProxyInstance方法返回的對象賦值給一個interface,而後執行interface的邏輯。注意,interfaces中的邏輯纔是主要的邏輯,InvocationHandler中只是附加的邏輯。理清楚主次有利於理解Java的Proxy代理。

這裏再一次記錄一下從代碼中咱們知道的Proxy.newProxyInstance一些限制:

  1. 第2個參數interfaces的接口個數最多65535個
  2. 第2個參數interfaces必須都是接口
  3. 第2個參數interfaces的接口若是有非公共的接口,非公共接口只能在一個包中
  4. 第2個參數interfaces的接口不能重複
  5. 第2個參數interfaces的接口都能被第1個參數指定的loader加載

最後就是getProxyClass0方法中使用到的緩存和同步技巧的確值得學習。

相關文章
相關標籤/搜索