Java 有兩種代理方式,一種是靜態代理,另外一種是動態代理。對於靜態代理,其實就是經過依賴注入,對對象進行封裝,不讓外部知道實現的細節。不少 API 就是經過這種形式來封裝的。html
代理模式結構圖(圖片來自《大話設計模式》)java
下面看下二者在概念上的解釋:程序員
靜態代理類:由程序員建立或者由第三方工具生成,再進行編譯;在程序運行以前,代理類的.class文件已經存在了。設計模式
靜態代理類一般只代理一個類。緩存
靜態代理事先知道要代理的是什麼。多線程
動態代理類:在程序運行時,經過反射機制動態生成。app
動態代理類一般代理接口下的全部類。eclipse
動態代理事先不知道要代理的是什麼,只有在運行的時候才能肯定。ide
動態代理的調用處理程序必須事先InvocationHandler接口,及使用Proxy類中的newProxyInstance方法動態的建立代理類。工具
Java動態代理只能代理接口,要代理類須要使用第三方的CLIGB等類庫。
Java動態代理的優點是實現無侵入式的代碼擴展,也就是方法的加強;讓你能夠在不用修改源碼的狀況下,加強一些方法;在方法的先後你能夠作你任何想作的事情(甚至不去執行這個方法就能夠)。此外,也能夠減小代碼量,若是採用靜態代理,類的方法比較多的時候,得手寫大量代碼。
靜態代理的實例這裏就不說了,比較簡單。在 java 的 java.lang.reflect 包下提供了一個 Proxy 類和一個 InvocationHandler 接口,經過這個類和這個接口能夠生成 JDK 動態代理類和動態代理對象。下面講講動態代理的實現。
先定義一個接口:
public interface Person { void setName(String name); }
再定義一個學生 Student 類來實現 Person 接口,每個學生都有一個本身的名字:
public class Student implements Person { private String mName; public Student(String name) { mName = name; } public void setName(String name) { mName = name; } }
Student 類中,定義了一個私有變量 mName,用來表示 Student 的名字。接下去定義一個代理 handler,就是用來幫咱們處理代理的 :
public class PersonHandler<T> implements InvocationHandler { // 代理的目標對象 private T mTarget; public PersonHandler(T target) { mTarget = target; } @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { // 調用開始前的操做 ProxyUtil.start(); // 調用方法,經過反射的形式來調用 mTarget 的 method Object result = method.invoke(mTarget, objects); // 打印更名前的名字 ProxyUtil.log(objects[0].toString()); // 調用結束後的操做 ProxyUtil.finish(); return result; } }
能夠發現,咱們在調用代碼先後都作了一些操做,甚至能夠直接攔截該方法,不讓其運行。其中定義了一個 ProxyUtil 類,方便咱們作一些操做:
public class ProxyUtil { private static final String TAG = "ProxyUtil"; public static void start() { Log.d(TAG, "start: " + System.currentTimeMillis()); } public static void finish() { Log.d(TAG, "finish: " + System.currentTimeMillis()); } public static void log(String name) { Log.d(TAG, "log: " + name); } }
接下去開始編寫代理的實現:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //建立一個實例對象,這個對象是被代理的對象 Person zhangsan = new Student("張三"); //建立一個與代理對象相關聯的 InvocationHandler PersonHandler stuHandler = new PersonHandler<>(zhangsan); //建立一個代理對象 stuProxy 來代理 zhangsan,代理對象的每一個執行方法都會替換執行 Invocation 中的 invoke 方法 Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler); //代理執行 setName 的方法 stuProxy.setName("王五"); }
看下打印輸出:
能夠發現代理成功了。而且咱們在調用方式的以前以後,都作了一些操做。Spring 的 AOP 其就是經過動態代理的機制實現的。
其中,咱們將 stuProxy 的類名打印出來:
Log.d(TAG, "onCreate: " + stuProxy.getClass().getCanonicalName());
發現其名字居然是 $Proxy0。具體緣由下面會解釋。
上面咱們利用 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 對象是否爲空,爲空就拋出 NullPointerException 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) {
// 數量超過 65535 就拋出異常,665535 這個就不用說了吧 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 = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
關鍵點在於 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()); } } }
這裏會經過反射獲取接口的各類修飾符,包名等,而後根據規則命名代理類,最後調用 ProxyGenerator.generateProxyClass 生成了代理類。
ProxyGenerator.generateProxyClass 具體實如今 eclipse 上打開後,說是找不到源碼:
不過,從其餘地方找到了部分代碼:
public static byte[] generateProxyClass(final String name, Class[] interfaces) { ProxyGenerator gen = new ProxyGenerator(name, interfaces); // 這裏動態生成代理類的字節碼,因爲比較複雜就不進去看了 final byte[] classFile = gen.generateClassFile(); // 若是saveGeneratedFiles的值爲true,則會把所生成的代理類的字節碼保存到硬盤上 if (saveGeneratedFiles) { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { try { FileOutputStream file = new FileOutputStream(dotToSlash(name) + ".class"); file.write(classFile); file.close(); return null; } catch (IOException e) { throw new InternalError( "I/O exception saving generated file: " + e); } } }); } // 返回代理類的字節碼 return classFile; }
咱們能夠本身試試 ProxyGenerator.generateProxyClass 的功能。
public class ProxyGeneratorUtils { /** * 把代理類的字節碼寫到硬盤上 * @param path 保存路徑 */ public static void writeProxyClassToHardDisk(String path) { // 獲取代理類的字節碼 byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", Student.class.getInterfaces()); FileOutputStream out = null; try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
main 方法裏面進行調用 :
public class Main { public static void main(String[] args) { ProxyGeneratorUtils.writeProxyClassToHardDisk("$Proxy0.class"); } }
能夠發現,在根目錄下生成了一個 $Proxy0.class 文件,文件內容反編譯後以下:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import proxy.Person; public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m2; private static Method m3; private static Method m0; /** *注意這裏是生成代理類的構造方法,方法參數爲InvocationHandler類型,看到這,是否是就有點明白 *爲什麼代理對象調用方法都是執行InvocationHandler中的invoke方法,而InvocationHandler又持有一個 *被代理對象的實例,不由會想難道是....? 沒錯,就是你想的那樣。 * *super(paramInvocationHandler),是調用父類Proxy的構造方法。 *父類持有:protected InvocationHandler h; *Proxy構造方法: * protected Proxy(InvocationHandler h) { * Objects.requireNonNull(h); * this.h = h; * } * */ public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } //這個靜態塊原本是在最後的,我把它拿到前面來,方便描述 static { try { //看看這兒靜態塊兒裏面有什麼,是否是找到了giveMoney方法。請記住giveMoney經過反射獲得的名字m3,其餘的先無論 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("proxy.Person").getMethod("giveMoney", 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()); } } /** * *這裏調用代理對象的giveMoney方法,直接就調用了InvocationHandler中的invoke方法,並把m3傳了進去。 *this.h.invoke(this, m3, null);這裏簡單,明瞭。 *來,再想一想,代理對象持有一個InvocationHandler對象,InvocationHandler對象持有一個被代理的對象, *再聯繫到InvacationHandler中的invoke方法。嗯,就是這樣。 */ public final void giveMoney() throws { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } //注意,這裏爲了節省篇幅,省去了toString,hashCode、equals方法的內容。原理和giveMoney方法一毛同樣。 }
jdk 爲咱們的生成了一個叫 $Proxy0(這個名字後面的0是編號,有多個代理類會一次遞增)的代理類,這個類文件時放在內存中的,咱們在建立代理對象時,就是經過反射得到這個類的構造方法,而後建立的代理實例。經過對這個生成的代理類源碼的查看,咱們很容易能看出,動態代理實現的具體過程。
咱們能夠對 InvocationHandler 看作一箇中介類,中介類持有一個被代理對象,在 invoke 方法中調用了被代理對象的相應方法,而生成的代理類中持有中介類,所以,當咱們在調用代理類的時候,就是再調用中介類的 invoke 方法,經過反射轉爲對被代理對象的調用。
代理類調用本身方法時,經過自身持有的中介類對象來調用中介類對象的 invoke 方法,從而達到代理執行被代理對象的方法。也就是說,動態代理經過中介類實現了具體的代理功能。
生成的代理類:$Proxy0 extends Proxy implements Person,咱們看到代理類繼承了 Proxy 類,因此也就決定了 java 動態代理只能對接口進行代理,Java 的繼承機制註定了這些動態代理類們沒法實現對 class 的動態代理。
參考文章: