這篇文章主要介紹了Java 動態代理深入理解的相關資料,需要的朋友可以參考下
要想了解Java動態代理,首先要了解什麼叫做代理,熟悉設計模式的朋友一定知道在Gof總結的23種設計模式中,有一種叫做代理(Proxy)的物件結構型模式,動態代理中的代理,指的就是這種設計模式。
在我看來所謂的代理模式,和23種設計模式中的「裝飾模式」是一個東西。 23種設計模式中將它們作為兩種模式,網路上也有些文章講這兩種模式的異同,從細節來看,確實可以人為地區分這兩種模式,但是抽像到一定高度後,我認為這兩種模式是完全一樣的。因此學會了代理模式,也就同時掌握了裝飾模式。
代理模式
代理模式簡單來說,就是對一個物件進行包裝,包裝後產生的物件具有和原始物件一樣的方法列表,但是每個方法都可以是包裝過的。
靜態代理
讓我們先來看一段程式碼:
package common; public class Test { static interface Subject{ void sayHi(); void sayHello(); } static class SubjectImpl implements Subject{ @Override public void sayHi() { System.out.println("hi"); } @Override public void sayHello() { System.out.println("hello"); } } static class SubjectImplProxy implements Subject{ private Subject target; public SubjectImplProxy(Subject target) { this.target=target; } @Override public void sayHi() { System.out.print("say:"); target.sayHi(); } @Override public void sayHello() { System.out.print("say:"); target.sayHello(); } } public static void main(String[] args) { Subject subject=new SubjectImpl(); Subject subjectProxy=new SubjectImplProxy(subject); subjectProxy.sayHi(); subjectProxy.sayHello(); } }
這段程式碼中首先定義了一個Subject介面,介面中有兩個方法。
然後定義了SubjectImpl類別實作Subject介面並實作其中的兩個方法,到這裡肯定是沒問題的。
現在再定義一個SubjuectImplProxy類,也實作Subject介面。這個SubjectImplProxy類別的作用是包裝SubjectImpl類別的實例,它的內部定義一個變數target來保存一個SubjectImpl的實例。 SubjectImplProxy也實作了介面規定的兩個方法,並且在它的實作版本中,都呼叫了SubjectImpl的實現,但是又加入了自己的處理邏輯。
相信這段程式碼不難理解,它透過對SubjectImpl進行包裝,達到了為輸出內容添加前綴的功能。這種代理方式叫做靜態代理。
動態代理
從上面的演示中我們不難看出靜態代理的缺點:我們對SubjectImpl的兩個方法,是進行的相同的包裝,但是卻要在SubjectImplProxy裡把相同的包裝邏輯寫兩次,而且以後如果Subject接口再添加新的方法,SubjectImplProxy也必須要添加新的實現,儘管SubjectImplProxy對所有方法的包裝可能都是一樣的。
下面我把上面例子的靜態代理改成動態代理,我們來看一下差異:
package common; import java.lang.invoke.MethodHandle; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { static interface Subject{ void sayHi(); void sayHello(); } static class SubjectImpl implements Subject{ @Override public void sayHi() { System.out.println("hi"); } @Override public void sayHello() { System.out.println("hello"); } } static class ProxyInvocationHandler implements InvocationHandler{ private Subject target; public ProxyInvocationHandler(Subject target) { this.target=target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.print("say:"); return method.invoke(target, args); } } public static void main(String[] args) { Subject subject=new SubjectImpl(); Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject)); subjectProxy.sayHi(); subjectProxy.sayHello(); } }
只看main方法的話,只有第二行和之前的靜態代理不同,同樣是產生一個subjectProxy代理對象,只是產生的程式碼不同了。靜態代理程式是直接new 一個SubjectImplProxy的實例,而動態代理則呼叫了java.lang.reflect.Proxy.newProxyInstance()方法,我們來看這個方法的原始碼:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass(loader, interfaces); //获取代理类的Class /* * Invoke its constructor with the designated invocation handler. */ try { Constructor cons = cl.getConstructor(constructorParams); //constructorParams是写死的:{ InvocationHandler.class },上边返回的代理类Class一定是extends Proxy的,而Proxy有一个参数为InvocationHandler的构造函数 return cons.newInstance(new Object[] { h }); //这里通过构造函数将我们自己定义的InvocationHandler的子类传到代理类的实例里,当我们调用代理类的任何方法时, 实际上都会调用我们定义的InvocationHandler子类重写的invoke()函数 } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }
上面的Class> cl = getProxyClass(loader, interfaces); 呼叫的getProxyClass方法:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { if (interfaces.length > 65535) { //因为在class文件中,一个类保存的接口数量是用2个字节来表示的,因此java中一个类最多可以实现65535个接口 throw new IllegalArgumentException("interface limit exceeded"); } Class<?> proxyClass = null; /* collect interface names to use as key for proxy class cache */ String[] interfaceNames = new String[interfaces.length]; // for detecting duplicates Set<Class<?>> interfaceSet = new HashSet<>(); //验证interfaces里的接口是否能被类加载器加载,是否是接口,是否有重复的 for (int i = 0; i < interfaces.length; i++) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ String interfaceName = interfaces[i].getName(); Class<?> interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " 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.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } /* * Using string representations of the proxy interfaces as * keys in the proxy class cache (instead of their Class * objects) is sufficient because we require the proxy * interfaces to be resolvable by name through the supplied * class loader, and it has the advantage that using a string * representation of a class makes for an implicit weak * reference to the class. */ List<String> key = Arrays.asList(interfaceNames); //使用interfaces列表作为key缓存在cache里,也就是实现了相同interfaces的代理类只会创建加载一次 /* * Find or create the proxy class cache for the class loader. */ Map<List<String>, Object> cache; synchronized (loaderToCache) { cache = loaderToCache.get(loader); if (cache == null) { cache = new HashMap<>(); loaderToCache.put(loader, cache); } /* * This mapping will remain valid for the duration of this * method, without further synchronization, because the mapping * will only be removed if the class loader becomes unreachable. */ } /* * Look up the list of interfaces in the proxy class cache using * the key. This lookup will result in one of three possible * kinds of values: * null, if there is currently no proxy class for the list of * interfaces in the class loader, * the pendingGenerationMarker object, if a proxy class for the * list of interfaces is currently being generated, * or a weak reference to a Class object, if a proxy class for * the list of interfaces has already been generated. */ //看看缓存里有没有,如果有就直接取出来然后return,否则判断根据pendingGenerationMarker判断是否有其它线程正在生成当前的代理类, 如果有则cache.wait()等待,如果没有则创建。 synchronized (cache) { /* * Note that we need not worry about reaping the cache for * entries with cleared weak references because if a proxy class * has been garbage collected, its class loader will have been * garbage collected as well, so the entire cache will be reaped * from the loaderToCache map. */ do { Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class<?>) ((Reference) value).get(); } if (proxyClass != null) { // proxy class already generated: return it return proxyClass; } else if (value == pendingGenerationMarker) { // proxy class being generated: wait for it 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 { /* * No proxy class for this list of interfaces has been * generated or is being generated, so we will go and * generate it now. Mark it as pending generation. */ cache.put(key, pendingGenerationMarker); break; } } while (true); } //确认要生成的代理类所属的包,如果interfaces里所有接口都是public的,代理类所属包就是默认包; 如果有interface不是public,那么所有不是public的interface必须在一个包里否则报错。 try { String proxyPkg = null; // package to define proxy class in /* * 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 (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) { // if no non-public proxy interfaces, proxyPkg = ""; // use the unnamed package } { /* * Choose a name for the proxy class to generate. */ long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num; //生成代理类的名字,proxyPkg是上面确定下来的代理类所在的包名,proxyClassNamePrefix是写死的字符串“$Proxy”, num是一个全局唯一的long型数字,从0开始累积,每次生成新的代理类就+1,从这里也能看出生成的动态代理类的数量不能超过Long.maxValue /* * Verify that the class loader hasn't already * defined a class with the chosen name. */ /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); //生成一个以proxyName为类名的,实现了Interfaces里所有接口的类的字节码 try { proxyClass = 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()); } } // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null); } finally { /* * We must clean up the "pending generation" state of the proxy * class cache entry somehow. If a proxy class was successfully * generated, store it in the cache (with a weak reference); * otherwise, remove the reserved entry. In all cases, notify * all waiters on reserved entries in this cache. */ //创建成功,则将cache中该key的pendingGenerationMarker替换为实际的代理类的弱引用,否则也要清除pendingGenerationMarker标记; 不管是否成功,都要执行cache.notifyAll(),让其它要创建相同代理类并且执行了cache.wait()的线程恢复执行。 synchronized (cache) { if (proxyClass != null) { cache.put(key, new WeakReference<Class<?>>(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } return proxyClass; //最后返回代理类Class }
到這裡,我們已經把動態代理的java原始碼都解析完了,現在思路就很清晰了:
Proxy.newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h) 方法簡單來說執行了以下操作:
1.產生一個實作了參數interfaces裡所有介面且繼承了Proxy的代理類別的字節碼,然後用參數裡的classLoader載入這個代理類別。
2.使用代理程式類別父類別的建構子 Proxy(InvocationHandler h)來創造一個代理類別的實例,將我們自訂的InvocationHandler的子類別傳入。
3.傳回這個代理類別實例,因為我們建構的代理類別實作了interfaces(也就是我們程式中傳入的subject.getClass().getInterfaces())裡的所有接口,因此傳回的代理類別可以強轉成Subject類型來呼叫介面中定義的方法。
現在我們知道了用Proxy.newProxyInstance()回傳的subjectProxy可以成功強轉成Subject類型來呼叫介面中定義的方法了,那麼在呼叫方法後,代理類別實例怎麼進行處理的呢,這就需要看一下代理類別的源碼了。但是代理類別是程式動態產生字節碼載入的,怎麼看源碼呢?沒關係,可以在main方法中加入System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”),這樣就會把產生的代理類別Class檔案儲存在本機磁碟上,然後再反編譯可以得到代理類別的原始碼:
package common; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Test.Subject { private static Method m4; private static Method m1; private static Method m3; private static Method m0; private static Method m2; static { try { m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("Test$Subject").getMethod("sayHi", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); } catch (Exception e) { throw new RuntimeException(e); } } public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final void sayHello() { try { this.h.invoke(this, m4, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final boolean equals(Object paramObject) { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void sayHi() { try { this.h.invoke(this, m3, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } }
我们可以看到代理类内部实现比较简单,在调用每个代理类每个方法的时候,都用反射去调h的invoke方法(也就是我们自定义的InvocationHandler的子类中重写的invoke方法),用参数传递了代理类实例、接口方法、调用参数列表,这样我们在重写的invoke方法中就可以实现对所有方法的统一包装了。
总结
动态代理相对于静态代理在使用上的优点主要是能够对一个对象的所有方法进行统一包装,而且后期被代理的类添加方法的时候动态代理类不需要改动。
缺点是要求被代理的类必须实现了接口,因为动态代理类在实现的时候继承了Proxy类,java不支持多继承,因此动态代理类只能根据接口来定义方法。
最后动态代理之所以叫做动态代理是因为java在实现动态代理的时候,动态代理类是在运行时动态生成和加载的,相对的,静态代理类和其他普通类一下,在类加载阶段就加载了。
以上是Java 動態代理程式範例程式碼深入理解的詳細內容。更多資訊請關注PHP中文網其他相關文章!