1. 程式人生 > >Java JDK 動態代理使用及實現原理分析

Java JDK 動態代理使用及實現原理分析

一、什麼是代理?

代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。

代理模式 UML 圖:

代理模式 UML 圖

簡單結構示意圖:

為了保持行為的一致性,代理類和委託類通常會實現相同的介面,所以在訪問者看來兩者沒有絲毫的區別。通過代理類這中間一層,能有效控制對委託類物件的直接訪問,也可以很好地隱藏和保護委託類物件,同時也為實施不同控制策略預留了空間,從而在設計上獲得了更大的靈活性。Java 動態代理機制以巧妙的方式近乎完美地實踐了代理模式的設計理念。

二、Java 動態代理類

Java 動態代理類位於 java.lang.reflect 包下,一般主要涉及到以下兩個類:

(1)Interface InvocationHandler:該介面中僅定義了一個方法

public Object invoke(Object obj,Method method, Object[] args)

在實際使用時,第一個引數 obj 一般是指代理類,method 是被代理的方法,如上例中的 request(),args 為該方法的引數陣列。這個抽象方法在代理類中動態實現。

(2)Proxy:該類即為動態代理類,其中主要包含以下內容:

  • protected Proxy(InvocationHandler h):建構函式,用於給內部的 h 賦值。
  • static Class getProxyClass (ClassLoaderloader, Class[] interfaces)
    :獲得一個代理類,其中 loader 是類裝載器,interfaces 是真實類所擁有的全部介面的陣列。
  • static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)返回代理類的一個例項,返回後的代理類可以當作被代理類使用 (可使用被代理類的在 Subject 介面中宣告過的方法)

所謂 DynamicProxy 是這樣一種 class:它是在執行時生成的 class,在生成它時你必須提供一組 interface 給它,然後該 class 就宣稱它實現了這些 interface

。你當然可以把該 class 的例項當作這些 interface 中的任何一個來用。當然,這個 DynamicProxy 其實就是一個 Proxy,它不會替你作實質性的工作, 在生成它的例項時你必須提供一個 handler,由它接管實際的工作

在使用動態代理類時,我們必須實現 InvocationHandler 介面

通過這種方式,被代理的物件 (RealSubject) 可以在執行時動態改變,需要控制的介面 (Subject 介面) 可以在執行時改變,控制的方式 (DynamicSubject 類) 也可以動態改變,從而實現了非常靈活的動態代理關係。

動態代理步驟:

  1. 建立一個實現介面 InvocationHandler 的類,它必須實現 invoke 方法
  2. 建立被代理的類以及介面
  3. 通過 Proxy 的靜態方法 newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h) 建立一個代理
  4. 通過代理呼叫方法

三、JDK 的動態代理怎麼使用?

1、需要動態代理的介面:

/**
 * 需要動態代理的介面
 */
public interface Subject
{
    /**
     * 你好
     *
     * @param name
     * @return
     */
    public String SayHello(String name);
 
    /**
     * 再見
     *
     * @return
     */
    public String SayGoodBye();
}

2、需要代理的實際物件

/**
 * 實際物件
 */
public class RealSubject implements Subject
{
 
    /**
     * 你好
     *
     * @param name
     * @return
     */
    public String SayHello(String name)
    {
        return "hello " + name;
    }
 
    /**
     * 再見
     *
     * @return
     */
    public String SayGoodBye()
    {
        return " good bye ";
    }
}

3、呼叫處理器實現類(有木有感覺這裡就是傳說中的 AOP 啊)

/**
 * 呼叫處理器實現類
 * 每次生成動態代理類物件時都需要指定一個實現了該介面的呼叫處理器物件
 */
public class InvocationHandlerImpl implements InvocationHandler
{
 
    /**
     * 這個就是我們要代理的真實物件
     */
    private Object subject;
 
    /**
     * 構造方法,給我們要代理的真實物件賦初值
     *
     * @param subject
     */
    public InvocationHandlerImpl(Object subject)
    {
        this.subject = subject;
    }
 
    /**
     * 該方法負責集中處理動態代理類上的所有方法呼叫。
     * 呼叫處理器根據這三個引數進行預處理或分派到委託類例項上反射執行
     *
     * @param proxy  代理類例項
     * @param method 被呼叫的方法物件
     * @param args   呼叫引數
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        //在代理真實物件前我們可以新增一些自己的操作
        System.out.println("在呼叫之前,我要乾點啥呢?");
 
        System.out.println("Method:" + method);
 
        //當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫
        Object returnValue = method.invoke(subject, args);
 
        //在代理真實物件後我們也可以新增一些自己的操作
        System.out.println("在呼叫之後,我要乾點啥呢?");
 
        return returnValue;
    }
}

4、測試

/**
 * 動態代理演示
 */
public class DynamicProxyDemonstration
{
    public static void main(String[] args)
    {
        //代理的真實物件
        Subject realSubject = new RealSubject();
        
        /**
         * InvocationHandlerImpl 實現了 InvocationHandler 介面,並能實現方法呼叫從代理類到委託類的分派轉發
         * 其內部通常包含指向委託類例項的引用,用於真正執行分派轉發過來的方法呼叫.
         * 即:要代理哪個真實物件,就將該物件傳進去,最後是通過該真實物件來呼叫其方法
         */
        InvocationHandler handler = new InvocationHandlerImpl(realSubject);
 
        ClassLoader loader = realSubject.getClass().getClassLoader();
        Class[] interfaces = realSubject.getClass().getInterfaces();
        /**
         * 該方法用於為指定類裝載器、一組介面及呼叫處理器生成動態代理類例項
         */
        Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
 
        System.out.println("動態代理物件的型別:"+subject.getClass().getName());
 
        String hello = subject.SayHello("jiankunking");
        System.out.println(hello);
//        String goodbye = subject.SayGoodBye();
//        System.out.println(goodbye);
    }
 
}

5、輸出結果如下: 輸出結果

四、動態代理怎麼實現的?

從使用程式碼中可以看出,關鍵點在:

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

通過跟蹤提示程式碼可以看出:當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的 handler 物件的 invoke 方法來進行呼叫。 也就是說,當代碼執行到: subject.SayHello(“jiankunking”) 這句話時,會自動呼叫 InvocationHandlerImpl 的 invoke 方法。這是為啥呢?

====================== 橫線之間的是程式碼跟分析的過程,不想看的朋友可以直接看結論 ====================== 以下程式碼來自: JDK1.8.0_92 既然生成代理物件是用的 Proxy 類的靜態方 newProxyInstance,那麼我們就去它的原始碼裡看一下它到底都做了些什麼?

/**
 * Returns an instance of a proxy class for the specified interfaces
 * that dispatches method invocations to the specified invocation
 * handler.
 *
 * <p>{@code Proxy.newProxyInstance} throws
 * {@code IllegalArgumentException} for the same reasons that
 * {@code Proxy.getProxyClass} does.
 *
 * @param   loader the class loader to define the proxy class
 * @param   interfaces the list of interfaces for the proxy class
 *          to implement
 * @param   h the invocation handler to dispatch method invocations to
 * @return  a proxy instance with the specified invocation handler of a
 *          proxy class that is defined by the specified class loader
 *          and that implements the specified interfaces
 * @throws  IllegalArgumentException if any of the restrictions on the
 *          parameters that may be passed to {@code getProxyClass}
 *          are violated
 * @throws  SecurityException if a security manager, <em>s</em>, is present
 *          and any of the following conditions is met:
 *          <ul>
 *          <li> the given {@code loader} is {@code null} and
 *               the caller's class loader is not {@code null} and the
 *               invocation of {@link SecurityManager#checkPermission
 *               s.checkPermission} with
 *               {@code RuntimePermission("getClassLoader")} permission
 *               denies access;</li>
 *          <li> for each proxy interface, {@code intf},
 *               the caller's class loader is not the same as or an
 *               ancestor of the class loader for {@code intf} and
 *               invocation of {@link SecurityManager#checkPackageAccess
 *               s.checkPackageAccess()} denies access to {@code intf};</li>
 *          <li> any of the given proxy interfaces is non-public and the
 *               caller class is not in the same {@linkplain Package runtime package}
 *               as the non-public interface and the invocation of
 *               {@link SecurityManager#checkPermission s.checkPermission} with
 *               {@code ReflectPermission("newProxyInPackage.{package name}")}
 *               permission denies access.</li>
 *          </ul>
 * @throws  NullPointerException if the {@code interfaces} array
 *          argument or any of its elements are {@code null}, or
 *          if the invocation handler, {@code h}, is
 *          {@code null}
 */
@CallerSensitive 
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) throws IllegalArgumentException {
	//檢查h 不為空,否則拋異常
    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);
        }
		//獲取代理物件的構造方法(也就是$Proxy0(InvocationHandler h)) 
        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;
                }
            });
        }
		//生成代理類的例項並把InvocationHandlerImpl的例項傳給它的構造方法
        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);
    }
}

我們再進去 getProxyClass0 方法看一下:

 /**
  * 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) {
        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);
}

真相還是沒有來到,繼續,看一下 proxyClassCache

/**
 * a cache of proxy classes
 */
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

奧,原來用了一下快取啊 那麼它對應的 get 方法啥樣呢?

 /**
 * Look-up the value through the cache. This always evaluates the
 * {@code subKeyFactory} function and optionally evaluates
 * {@code valueFactory} function if there is no entry in the cache for given
 * pair of (key, subKey) or the entry has already been cleared.
 *
 * @param key       possibly null key
 * @param parameter parameter used together with key to create sub-key and
 *                  value (should not be null)
 * @return the cached value (never null)
 * @throws NullPointerException if {@code parameter} passed in or
 *                              {@code sub-key} calculated by
 *                              {@code subKeyFactory} or {@code value}
 *                              calculated by {@code valueFactory} is null.
 */
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) {
		//putIfAbsent這個方法在key不存在的時候加入一個值,如果key存在就不放入
        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);
            }
        }
    }
}

我們可以看到它呼叫了 supplier.get(); 獲取動態代理類,其中 supplier 是 Factory, 這個類定義在 WeakCach 的內部。 來瞅瞅,get 裡面又做了什麼?

 public synchronized V get() { // serialize access
    // re-check
    Supplier<V> supplier = valuesMap.get(subKey);
    if (supplier != this) {
        // something changed while we were waiting:
        // might be that we were replaced by a CacheValue
        // or were removed because of failure ->
        // return null to signal WeakCache.get() to retry
        // the loop
        return null;
    }
    // else still us (supplier == this)

    // create new value
    V value = null;
    try {
        value = Objects.requireNonNull(valueFactory.apply(key, parameter));
    } finally {
        if (value == null) { // remove us on failure
            valuesMap.remove(subKey, this);
        }
    }
    // the only path to reach here is with non-null value
    assert value != null;

    // wrap value with CacheValue (WeakReference)
    CacheValue<V> cacheValue = new CacheValue<>(value);

    // try replacing us with CacheValue (this should always succeed)
    if (valuesMap.replace(subKey, this, cacheValue)) {
        // put also in reverseMap
        reverseMap.put(cacheValue, Boolean.TRUE);
    } else {
        throw new AssertionError("Should not reach here");
    }

    // successfully replaced us with new CacheValue -> return the value
    // wrapped by it
    return value;
}

發現重點還是木有出現,但我們可以看到它呼叫了 valueFactory.apply(key, parameter) 方法:

/**
 * 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 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.
         */