動態代理三部曲(一):動態代理模式及實現原理


一、背景

代理模式,作為常用的設計模式之一,在實際項目中或多或少都會被使用到。當前一些主流的項目框架中,也有不少代理模式的身影。

代理模式中,代理類與主體類實現同樣的接口,代理類持有實體類的引用,並接受客戶端對代理類中實體引用的外部注入,並代理實體類的功能。
注:描述中的這種外部注入形式有個專有技術名詞:依賴注入

代理模式通用的類圖為:

 

 

按照代理類的產生方式,是在運行期之前就靜態的存在還是在運行期動態產生,可以將代理模式分為靜態代理和動態代理。


二、靜態代理

在真正理解動態代理之前,有必要先簡單回顧下靜態代理的一般過程。

直接看一個具體的實例。
1,定義接口:

package com.corn.proxy.pstatic;

public interface Subject {

    String action();

}
復制代碼

2,定義主體類:

package com.corn.proxy.pstatic;

public class RealSubject implements Subject {

    @Override
    public String action() { System.out.println("action in RealSubject"); return "action done"; } } 復制代碼

3,定義靜態代理類:

package com.corn.proxy.pstatic;

public class ProxySubject implements Subject{

    private Subject realSubject;

    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public String action() { // 主體action前執行 System.out.println("do sth before RealSubject action"); String result = this.realSubject.action(); // 主體action后執行 System.out.println("do sth after RealSubject action"); return result; } } 復制代碼

4,客戶端注入實體並訪問:

package com.corn.proxy.pstatic;


public class Client {

    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);

        proxySubject.action();
    }
}
復制代碼

run,輸出結果:

do sth before RealSubject action action in RealSubject do sth after RealSubject action 復制代碼

上述代碼過程亦對應了代理模式類圖結構。


三、動態代理

本質上,動態代理也是遵循上述通用的代理模式類圖關系,與靜態代理相比,其動態主要體現在: 1,具體代理類(ProxySubject)的生成是在運行期動態產生的,而非編譯期就已經靜態存在;
2,具體代理類(ProxySubject)與被代理類的代理關系(ProxySubject持有RealSubject的引用),是想辦法動態注入進入的;
3,具體代理類(ProxySubject)對被代理類的功能的代理是在動態生成的代理類內部,想辦法去動態的調用被代理類的對應方法的。

無論是具體代理類的動態產生,還是與被代理類的關系建立,以及對被代理類方法的代理調用,這中間,都用到了兩個關鍵的中間媒介,即Proxy和InvocationHandler。

Proxy類,其中提供了動態生成代理類的靜態方法,並持有實現了InvocationHandler接口的引用。同時,所有生成的代理類也都是Proxy類的子類。

InvocationHandler接口,只包含一個抽象出來的方法名:invoke,使得實現InvocationHandler接口的類去具體實現,在實現中通過持有被代理類實體(RealSubject),並通過反射,去調用對應的實體方法。

因此,動態代理總體上的執行流程為:
當客戶端通過Proxy的靜態方法生成動態代理類后,調用動態代理類對應的接口方法時,內部會調用其內部持有的InvocationHandler接口的實例對象的invoke方法,並得以調用到實際被代理實體的相應方法。

將總體的類之間關系如果用類圖表示,與通用的代理模式類圖稍有區別。

 

相應代碼實現代碼過程如下:
1,定義接口Subject:

package com.corn.proxy.pdynamic;

public interface Subject {

    String action();

}
復制代碼

2,定義主體類:

package com.corn.proxy.pdynamic;

public class RealSubject implements Subject{

    @Override
    public String action() { System.out.println("action in RealSubject"); return "action done"; } } 復制代碼

3,定義實現了InvocationHandler接口的類:

package com.corn.proxy.pdynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ProxyInvocationHandler implements InvocationHandler {

    protected Subject subject;

    public ProxyInvocationHandler(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("do something before in ProxyInvocationHandler"); return method.invoke(subject, args); } } 復制代碼

4,客戶端注入實體並訪問:

package com.corn.proxy.pdynamic;

import java.lang.reflect.Proxy;

public class Client {

    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(realSubject);

        Subject proxyRealSubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(), proxyInvocationHandler);

        proxyRealSubject.action();
    }
}
復制代碼

run,輸出結果:

do something before in ProxyInvocationHandler action in RealSubject 復制代碼

四、實現原理

從上述動態代理執行流程和類圖分析中已經可以看出,動態代理的關鍵,在於通過InvocationHandler和Proxy媒介,在運行時動態生成動態代理類,生成的動態代理類依然實現了Subject接口,並在調用方法時回調了InvocationHandler實現類的invoke方法,InvocationHandler實現類的invoke方法通過反射,回調了被代理實體的對應方法。

看起來有點繞。

在生成動態代理過程中,Java工程和Android項目中有點區別,先從源碼角度看下Java工程的具體實現過程。

Android studio中不能直接新建Java工程,但可以在Android項目中通過建立Java Library的方式,建立Java庫。默認情況下,Android Studio關聯的JDK為AS內置的,是看不到源碼的,為此,需要將其改成自己下載的JDK路徑,此路中包含了源碼src目錄。 具體修改方式為:
1,查看本地JDK目錄

/usr/libexec/java_home -V
復制代碼

輸出

Matching Java Virtual Machines (1):
    1.8.0_162, x86_64:	"Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home 復制代碼

2,Android Studio替換JDK設置 File >> Other Setting >> Default Project Structure,在JDK Location中將默認設置

/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home
復制代碼

修改為:

/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home
復制代碼

完成設置后,可以直接看到JDK源碼。

ProxyInvocationHandler接口只有一個方法:

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); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { 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}); } 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); } 復制代碼

於是,我們跟到對應的ProxyClassFactory:

/**
 * 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. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } 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()); } } } 復制代碼

其中,關鍵的過程在於:

/*
 * Generate the specified proxy class.
 */
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);
try {
    return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { ... } 復制代碼

通過ProxyGenerator類中的generateProxyClass方法生成對應的動態代理類二進制代碼,並通過 ClassLoader加載后,通過反射,生成對應的類實例。

ProxyGenerator類是直接集成在rt.jar中的,包名為sun.misc,為擴展類。Android項目中因JDK版本問題,默認是不集成的,那Android中此處是如何實現的呢?

同樣的,在Android項目中直接使用動態代理,跟蹤源碼調用過程。

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

    final Class<?>[] intfs = interfaces.clone();
    // Android-removed: SecurityManager calls
    /*
    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-removed: SecurityManager / permission checks. /* if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } */ final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { // BEGIN Android-changed: Excluded AccessController.doPrivileged call. /* AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); */ cons.setAccessible(true); // END Android-removed: Excluded AccessController.doPrivileged call. } 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); } } 復制代碼

很明顯,Android JDK中newProxyInstance方法中部分實現做了修改。但主要執行路徑依然不變。

/**
 * 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); } 復制代碼

同樣的,ProxyClassFactory類。

/**
 * 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); } } } 復制代碼

其中關鍵的,我們發現

// Android-changed: Generate the proxy directly instead of calling
// through to ProxyGenerator.
...
return generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray); 復制代碼

跟蹤進去看下具體的執行過程

@FastNative
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
                                             ClassLoader loader, Method[] methods,
                                             Class<?>[][] exceptions);
// END Android-changed: How proxies are generated.
復制代碼

原來,Android JDK中對generateProxy進行了處理,直接使用的是本地的方法。

那么,生成動態代理時,如果遇到線程安全問題呢?
ProxyClassFactory代碼中,有如下處理過程:

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(); ... /* * 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, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { ... } } 復制代碼

顯然,通過線程安全的文件命名方式,預防了針對同樣的接口使用多線程情況下,使用動態代理可能出現的線程安全問題。

那么,生成的動態代理到底長什么樣子?我們可以直接調用系統的ProxyGenerator.generateProxyClass試一下。

package com.corn.proxy.pdynamic;


import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;

public class Client {

    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(realSubject);

        Subject proxyRealSubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(), proxyInvocationHandler);

        proxyRealSubject.action();

        String proxyName = "ProxySubject"; byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, RealSubject.class.getInterfaces()); try (FileOutputStream fos = new FileOutputStream("/Users/corn/T/ProxySubject.class")){ fos.write(proxyClassFile); fos.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } 復制代碼

run,對應目錄下生成ProxySubject.class文件,通過工具可以看到對應的字節碼內容。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.corn.proxy.pdynamic.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

    public ProxySubject(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String action() throws { try { return (String)super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m3 = Class.forName("com.corn.proxy.pdynamic.Subject").getMethod("action"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } } 復制代碼

果然,在生成的動態代理中,實現了Subject接口,並在對應的action方法中,調用了InvocationHandler實現類的實例的invoke方法。


五、結語

動態代理作為代理模式中的一種,避免了在運行期之前直接定義靜態代理類,對於需要大量的代理類的情形下,是非常有用的。同時,我們也應該看到,在整體流程上,其實動態代理與靜態代理總體上還是一樣的。動態代理無論在代理類的創建過程中,還是對代理方法的調用,過程中都用到了反射,在一定程度上性能上有所損耗,實際使用中需要適量權衡。

動態代理模式,作為設計模式中相對比較難理解的一種,主要在於其過程經過了層層封裝,最后只是通過Proxy類和InvocationHandler對外直接暴露了使用接口,對使用方直接屏蔽了具體的細節。但對於我們理解這種模式本身來說,了解並適當掌握其中的過程,還是有所受益的。


作者:HappyCorn
鏈接:https://juejin.im/post/5d047101f265da1bb2772e09
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM