jdk動態代理實現總結和範例

最近在研究spring aop,其中代理在aop扮演着一個很重要的角色,現在來總結一下動態代理(這裏只總結動態代理,代理模式和靜態代理略)。

動態代理是跟靜態代理的目的都一樣,代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後處理消息等。代理類與委託類之間通常會存在關聯關係,代理類的對象本身並不真正實現服務,而是通過調用委託類的對象的相關方法,來提供特定的服務。

先來看看動態代理的一個簡單的例子:
業務接口ITalk

public interface ITalk {
    void talk();
}

業務實現類 People

public class People implements ITalk {
    @Override
    public void talk() {
        System.out.println("people talk");
    }
}

代理類MyProxy

public class MyProxy implements InvocationHandler {
    private Object target;

    public Object bind(Object target) throws NoSuchMethodException, SecurityException{
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        System.out.println("切面之前業務");
        result = method.invoke(target, args);
        System.out.println("切面之後業務");
        return result;
    }
}

客戶端代碼:

public static void main(String[] args)  {
        ITalk talk = (ITalk)new MyProxy().bind(new People());
        talk.talk();

    }

輸出結果爲:
主業務之前做的事
people talk
主業務之後做的事

執行完畢,似乎一切都很神奇,爲什麼People的talk方法的多輸出了‘主業務之前做的事’和‘主業務之後做的事’?不是隻有‘people talk’嗎?這就是動態代理的威力······

調試一下程序,可以發現客戶端代碼中的talk引用的對象是$Proxy0,由此我們可以知道,這個對象跟Proxy有很大的聯繫。

jdk中動態代理的實現有兩個重要的角色:java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。
InvocationHandler接口:

public interface InvocationHandler {
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}

Proxy的newProxyInstance方法:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        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);

         if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            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;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
    }

神奇的事情都發生在Proxy的newProxyInstance方法,裏面調用了一個重要的方法getProxyClass0()

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        return proxyClassCache.get(loader, interfaces);
    }

繼續往proxyClassCache.get(loader, interfaces)裏探究,最後發現Proxy的一個內部類ProxyClassFactory中的apply方法

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }

                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;

            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) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;


            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {

                throw new IllegalArgumentException(e.toString());
            }
        }
    }

細細研究,其實可以發現Proxy.newProxyInstance的作用如下:
1、根據傳入的interfaces參數動態產生一個類,並實現interfaces中的接口,上面的例子就是生成的動態類實現了ITalk。並且繼承了Proxy類,重寫hashcode,equals,toString方法,具體實現在ProxyGenerator.generateProxyClass(…); 並生成了$Proxy0類

2,通過傳入的classloader將剛產生的類加載到jvm中。

3,、第三個參數爲InvocationHandler,調用新產生的$Proxy0的構造函數,Proxy(InvocationHandle h),並且用interfaces參數遍歷其所有接口的方法,並生成Method對象初始化對象的幾個Method成員變量
4、將Proxy0的實例返回給客戶端。

因爲Proxy0實現了ITalk,所以可以轉化爲ITalk類型,並且調用ITalk的talk方法,而這裏,其實也就是調用了InvocationHandle中的invoke方法:
在$Proxy0中的talk方法:

public final void talk{
try {
    super.h.invoke(this, m3, null); //該段則執行了InvocationHandler.invoke();  super.h既是InvocationHandler
    return;
   } catch (Error e) {
   } catch (Throwable throwable) {
    throw new UndeclaredThrowableException(throwable);
   }
 }

至此,就可以接受不同的委託而進行動態的代理了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章