一:經常使用的java代理模式
通常常常作java開發的知道java的代理模式一共有三種,第一種也就是靜態代理,這種用法比較簡單,沒有什麼魔法棒,比較好理解,另外兩種分別是JDK代理和cglib代理,他們分別是對接口代理和對class類自己進行代理,jdk代理要求類必須實現有一個或者多個接口,對接口進行字節碼加強在內存中實現新的class類去反射調用用戶target的實現類,這裏須要說明的是不論是cglic代理也好仍是jdk代理他們在內存中都要佔據方法區資源(jdk8 叫原空間),從而達到代理目的,而cglib代理是對class類自己進行字節碼加強配合fastclass來實現代理,關於更多的cglib和jdk代理相關的內容你們能夠google搜索一下,網上有不少這裏不作再多的說明。下面咱們摒棄jdk,和cglib的複雜源碼來本身實現一個代理模式,來更深入的瞭解一下代理到底是怎麼造成的。html
二:java原生jdk代理demo和源碼分析
②被代理類實現業務接口;
③定義代理類並實現業務接口;
④最後即可經過客戶端進行調用。(這裏能夠理解成程序的main方法裏的內容)
咱們按照這個步驟去實現靜態代理。需求:在向數據庫添加一個用戶時先後打印日誌。
public interface IUserService { void add(String name); }
UserServiceImpl.java java
public class UserServiceImpl implements IUserService{ @Override public void add(String name) { System.out.println("數據庫中插入: "+name+" 的用戶"); } }
MyInvocationHandler.javaspring
public class MyInvocationHandler implements InvocationHandler { //被代理對象,Object類型 private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("準備向數據庫中插入數據"); Object returnvalue = method.invoke(target, args); System.out.println("插入數據庫成功"); return returnvalue; } }
測試類數據庫
public static void main(String[] args) { IUserService target = new UserServiceImpl(); MyInvocationHandler handler = new MyInvocationHandler(target); IUserService proxyObject = (IUserService) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), target.getClass().getInterfaces(), handler); proxyObject.add("張玉龍"); }
使用上很是簡單、網上demo也不少,不作充分講解,對jdk代理用法的小夥伴若是還不熟悉這塊代碼,就先了解一下jdk代理的使用方式,而後在回來繼續看下面的源碼分析數組
JDK代理源碼深度分析
Proxy.newProxyInstance( ClassLoaderloader, Class[] interfaces, InvocationHandler h)
產生了代理對象,因此咱們進到
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(loader, intfs)
獲得代理類的Class對象,而後經過Class對象獲得構造方法,進而建立代理對象。下一步看
getProxyClass0
這個方法。
//此方法也是Proxy類下的方法 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 //意思是:若是代理類被指定的類加載器loader定義了,並實現了給定的接口interfaces, //那麼就返回緩存的代理類對象,不然使用ProxyClassFactory建立代理類。 return proxyClassCache.get(loader, interfaces); }
這裏看到proxyClassCache,有Cache便知道是緩存的意思,正好呼應了前面Look up or generate the designated proxy class。查詢(在緩存中已經有)或生成指定的代理類的class對象這段註釋。緩存
proxyClassCache
是個WeakCache類的對象,調用proxyClassCache.get(loader, interfaces); 能夠獲得緩存的代理類或建立代理類(沒有緩存的狀況)。說明WeakCache中有
get
這個方法。先看下WeakCache類的定義(這裏先只給出變量的定義和構造函數):
//K表明key的類型,P表明參數的類型,V表明value的類型。 // WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache 說明proxyClassCache存的值是Class<?>對象,正是咱們須要的代理類對象。 final class WeakCache<K, P, V> { private final ReferenceQueue<K> refQueue = new ReferenceQueue<>(); // the key type is Object for supporting null key private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>(); private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>(); private final BiFunction<K, P, ?> subKeyFactory; private final BiFunction<K, P, V> valueFactory; public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) { this.subKeyFactory = Objects.requireNonNull(subKeyFactory); this.valueFactory = Objects.requireNonNull(valueFactory); }
(key, sub-key) -> value
。其中key是傳進來的Classloader進行包裝後的對象,sub-key是由WeakCache構造函數傳人的
KeyFactory()
生成的。value就是產生代理類的對象,是由WeakCache構造函數傳人的
ProxyClassFactory()
生成的
好,大致上說完WeakCache這個類的做用,咱們回到剛纔
proxyClassCache.get(loader, interfaces);
這句代碼。get是WeakCache裏的方法。源碼以下
//K和P就是WeakCache定義中的泛型,key是類加載器,parameter是接口類數組 public V get(K key, P parameter) { //檢查parameter不爲空 Objects.requireNonNull(parameter); //清除無效的緩存 expungeStaleEntries(); // cacheKey就是(key, sub-key) -> value裏的一級key, Object cacheKey = CacheKey.valueOf(key, refQueue); // lazily install the 2nd level valuesMap for the particular cacheKey //根據一級key獲得 ConcurrentMap<Object, Supplier<V>>對象。若是以前不存在,則新建一個ConcurrentMap<Object, Supplier<V>>和cacheKey(一級key)一塊兒放到map中。 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { 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 //這部分就是調用生成sub-key的代碼,上面咱們已經看過怎麼生成的了 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //經過sub-key獲得supplier Supplier<V> supplier = valuesMap.get(subKey); //supplier實際上就是這個factory Factory factory = null; while (true) { //若是緩存裏有supplier ,那就直接經過get方法,獲得代理類對象,返回,就結束了,一下子分析get方法。 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 //下面的全部代碼目的就是:若是緩存中沒有supplier,則建立一個Factory對象,把factory對象在多線程的環境下安全的賦給supplier。 //由於是在while(true)中,賦值成功後又回到上面去調get方法,返回才結束。 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); } } } }
因此接下來咱們看Factory類中的get方法。安全
public synchronized V get() { // serialize access // re-check Supplier<V> supplier = valuesMap.get(subKey); //從新檢查獲得的supplier是否是當前對象 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 { //代理類就是在這個位置調用valueFactory生成的 //valueFactory就是咱們傳入的 new ProxyClassFactory() //一會咱們分析ProxyClassFactory()的apply方法 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) //把value包裝成弱引用 CacheValue<V> cacheValue = new CacheValue<>(value); // put into reverseMap // reverseMap是用來實現緩存的有效性 reverseMap.put(cacheValue, Boolean.TRUE); // try replacing us with CacheValue (this should always succeed) if (!valuesMap.replace(subKey, this, cacheValue)) { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it return value; } }
撥雲見日,來到ProxyClassFactory的apply方法,代理類就是在這裏生成的。多線程
//這裏的BiFunction<T, U, R>是個函數式接口,能夠理解爲用T,U兩種類型作參數,獲得R類型的返回值 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 //代理類訪問控制符: public ,final 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. */ //驗證全部非公共的接口在同一個包內;公共的就無需處理 //生成包名和類名的邏輯,包名默認是com.sun.proxy,類名默認是$Proxy 加上一個自增的整數值 //若是被代理類是 non-public proxy interface ,則用和被代理類接口同樣的包名 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(); //代理類的徹底限定名,如com.sun.proxy.$Proxy0.calss String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ //核心部分,生成代理類的字節碼 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { //把代理類加載到JVM中,至此動態代理過程基本結束了 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()); } } }
到這裏其實已經分析完了,可是本着深究的態度,決定看看JDK生成的動態代理字節碼是什麼,因而咱們將字節碼保存到磁盤上的class文件中。代碼以下:架構
public static void main(String[] args) { IUserService target = new UserServiceImpl(); MyInvocationHandler handler = new MyInvocationHandler(target); //第一個參數是指定代理類的類加載器(咱們傳入當前測試類的類加載器) //第二個參數是代理類須要實現的接口(咱們傳入被代理類實現的接口,這樣生成的代理類和被代理類就實現了相同的接口) //第三個參數是invocation handler,用來處理方法的調用。這裏傳入咱們本身實現的handler IUserService proxyObject = (IUserService) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), target.getClass().getInterfaces(), handler); proxyObject.add("張玉龍"); String path = "D:/$Proxy0.class"; byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", HelloworldImpl.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(); } } }
運行這段代碼,會在D盤生成一個名爲$Proxy0.class的文件。經過反編譯工具,獲得JDK爲咱們生成的代理類是這樣的:mvc
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://kpdus.tripod.com/jad.html // Decompiler options: packimports(3) fieldsfirst ansi space import com.zhb.jdk.proxy.IUserService; import java.lang.reflect.*; public final class $Proxy0 extends Proxy implements IUserService { private static Method m1; private static Method m2; private static Method m3; private static Method m0; //代理類的構造函數,其參數正是是InvocationHandler實例, //Proxy.newInstance方法就是經過經過這個構造函數來建立代理實例的 public $Proxy0(InvocationHandler invocationhandler) { super(invocationhandler); } // Object類中的三個方法,equals,toString, hashCode public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } //接口代理方法 public final void add(String s) { try { // invocation handler的 invoke方法在這裏被調用 super.h.invoke(this, m3, new Object[] { s }); return; } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { // 在這裏調用了invoke方法。 return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } // 靜態代碼塊對變量進行一些初始化工做 static { try { 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("com.zhb.jdk.proxy.IUserService").getMethod("add", new Class[] { Class.forName("java.lang.String") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch (ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }
生成了Object類的三個方法:toString,hashCode,equals。還有咱們須要被代理的方法。
JDK代理類的cache clear機制
你們都知道、在項目中被代理的class愈來愈多,因此jdk會搞一個cache的方式來防止相同的代理接口重複生成class,影響性能不說,實現也不是很優雅,那麼如今就會有一個問題了,當classloader已經在內存中沒有依賴的時候,被代理的proxy class其實也沒有什麼意義了,這樣就須要清空無用的cache,java Proxy採用了很是巧妙的「弱引用機制」,咱們來看下面的代碼
咱們仍是繼續看get方法的源碼
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) { ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } ....... }
其中源碼中有一個方法expungeStaleEntries、咱們進去這個方法一窺究竟
private void expungeStaleEntries() { CacheKey<K> cacheKey; while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) { cacheKey.expungeFrom(map, reverseMap); } }
在看看expungeFrom方法源碼幹了些什麼
void expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map, ConcurrentMap<?, Boolean> reverseMap) { // removing just by key is always safe here because after a CacheKey // is cleared and enqueue-ed it is only equal to itself // (see equals method)... ConcurrentMap<?, ?> valuesMap = map.remove(this); // remove also from reverseMap if needed if (valuesMap != null) { for (Object cacheValue : valuesMap.values()) { reverseMap.remove(cacheValue); } } }
代碼很清晰了,清空被代理的對象。如今的關鍵就是refQueue對象是怎麼來的。咱們繼續找一下跟refQueue相關的源碼、在get中還有一段代碼是這樣的
Object cacheKey = CacheKey.valueOf(key, refQueue);
private static final class CacheKey<K> extends WeakReference<K> { // a replacement for null keys private static final Object NULL_KEY = new Object(); static <K> Object valueOf(K key, ReferenceQueue<K> refQueue) { return key == null // null key means we can't weakly reference it, // so we use a NULL_KEY singleton as cache key ? NULL_KEY // non-null key requires wrapping with a WeakReference : new CacheKey<>(key, refQueue); } private final int hash; private CacheKey(K key, ReferenceQueue<K> refQueue) { super(key, refQueue); this.hash = System.identityHashCode(key); // compare by identity } ..... }
這樣看就很是清晰了、原來是CacheKey繼承了WeakReference弱引用機制,當弱引用依賴的key沒有引用的時候,當前失效的對象就會進入ReferenceQueue中來實現清空cache的功能、這種實現思路和ThreadLocal的實現原理是同樣的、你們有興趣能夠去閱讀如下相關源碼。
三:手把手寫基於接口的java代理
上面咱們分析了jdk動態代理源碼、那咱們是否是能夠本身用本身的方式去寫一個屬於本身的jdk代理呢,答案是能夠的
首先咱們寫一個基類,固然我並無在基類裏面寫什麼東西,只是模擬java中的proxy類而已,固然咱們也能夠豐富的去拓展一下這個類的方法,來實現更多的功能,讀者能夠經過讀完這篇文章以後本身去考慮一下如何來拓展。
1 package meituan.zylproxy.handlder; 2 public class ZylProxy { 3 public ZylProxy(){ 4 } 5 }
代理的核心接口,咱們去作代理的時候必定是經過反射去調用的,無論jdk也好仍是cglib也好,永遠也沒法脫離反射,咱們照貓畫虎,本身寫一個代理接口核心類,這並非什麼難題,看起來和jdk的核心類接口也沒有什麼區別。
1 package meituan.zylproxy.handlder; 2 3 import java.lang.reflect.Method; 4 5 public interface ZYLInvocationHandler { 6 7 public Object invoke(Object proxy, Method method, Object[] args) 8 throws Exception; 9 }
說明一下 第一個參數proxy是表明代理類,而不是用戶本身寫的原生類實現。參數Method是接口的方法,args是運行時參數列表,在運行時傳遞過來的實際上就是實現類的參數,好了,下面讓咱們去深刻核心。
咱們自定義兩個接口和接口的實現Idto,Idto2,和Dtoimpl以下:
1 package meituan.zylproxy.test.i; 2 3 public interface Idto { 4 5 public void add(); 6 7 public String get(); 8 9 }
package meituan.zylproxy.test.i; public interface Idto2 { public void adda(); public String geta(); }
package meituan.zylproxy.test.i.impl; import meituan.zylproxy.test.i.Idto; import meituan.zylproxy.test.i.Idto2; public class DtoImpl implements Idto,Idto2{ @Override public void add() { System.out.println("add"); } @Override public String get() { System.out.println("get"); return "return get"; } @Override public void adda() { System.out.println("adda"); } @Override public String geta() { System.out.println("geta"); return "return geta"; } }
這是幾個再簡單不過的接口和實現類了,也沒有什麼可說的。接下來咱們想對接口進行代理,無非是咱們動態將接口進行實現,從而達到對使用者進行自定義handle接口暴露而已,下面看一下咱們須要生成一個什麼樣的代理類。
import java.lang.reflect.Method; import meituan.zylproxy.handlder.ZylProxy; import meituan.zylproxy.handlder.ZYLInvocationHandler; import meituan.zylproxy.test.i.Idto; import meituan.zylproxy.test.i.Idto2; public class IdtoPorxy extends ZylProxy implements Idto, Idto2 { public ZYLInvocationHandler zYLInvocationHandler; public static Method add1; public static Method get2; public static Method adda3; public static Method geta4; static { try { add1 = Class.forName ( "meituan.zylproxy.test.i.Idto" ).getMethod ( "add", new Class[0] ); get2 = Class.forName ( "meituan.zylproxy.test.i.Idto" ).getMethod ( "get", new Class[0] ); adda3 = Class.forName ( "meituan.zylproxy.test.i.Idto2" ).getMethod ( "adda", new Class[0] ); geta4 = Class.forName ( "meituan.zylproxy.test.i.Idto2" ).getMethod ( "geta", new Class[0] ); } catch (Exception e) { } } public IdtoPorxy(ZYLInvocationHandler zYLInvocationHandler) { this.zYLInvocationHandler = zYLInvocationHandler; } public void add() { Object[] o = {}; try { this.zYLInvocationHandler.invoke ( this, add1, o ); return; } catch (Throwable e) { e.printStackTrace (); } } public java.lang.String get() { Object[] o = {}; try { return (java.lang.String) this.zYLInvocationHandler.invoke ( this, get2, o ); } catch (Exception e) { e.printStackTrace (); } return null; } public void adda() { Object[] o = {}; try { this.zYLInvocationHandler.invoke ( this, adda3, o ); return; } catch (Throwable e) { e.printStackTrace (); } } public java.lang.String geta() { Object[] o = {}; try { return (java.lang.String) this.zYLInvocationHandler.invoke ( this, geta4, o ); } catch (Exception e) { e.printStackTrace (); } return null; } }
這個類不是由用戶寫的,而是咱們動態生成的,對於jdk來講是生成了字節碼,對cglib來講是經過字節碼加強,其實實現的方式有多種,後面爲了更方便你們理解我用字符串的形式來動態生成這麼一個"傢伙",先看看這個類幹了些什麼吧,也很簡單。
public class IdtoPorxy extends ZylProxy implements Idto, Idto2
首先是繼承了剛纔咱們所說的ZylProxy,留着從此拓展,能夠參照java的Proxy,而後而且動態的實現了這兩個接口。很簡單
public ZYLInvocationHandler zYLInvocationHandler;
public IdtoPorxy(ZYLInvocationHandler zYLInvocationHandler) { this.zYLInvocationHandler = zYLInvocationHandler; }
這個是經過構造函數傳進來一個handler對象,對實現類的操做都靠它了。
public static Method add1; public static Method get2; public static Method adda3; public static Method geta4; static { try { add1 = Class.forName ( "meituan.zylproxy.test.i.Idto" ).getMethod ( "add", new Class[0] ); get2 = Class.forName ( "meituan.zylproxy.test.i.Idto" ).getMethod ( "get", new Class[0] ); adda3 = Class.forName ( "meituan.zylproxy.test.i.Idto2" ).getMethod ( "adda", new Class[0] ); geta4 = Class.forName ( "meituan.zylproxy.test.i.Idto2" ).getMethod ( "geta", new Class[0] ); } catch (Exception e) { } }
枚舉出來全部的接口的方法,經過class.forname來獲取到Method元數據。備用
public void add() { Object[] o = {}; try { this.zYLInvocationHandler.invoke ( this, add1, o ); return; } catch (Throwable e) { e.printStackTrace (); } } public java.lang.String get() { Object[] o = {}; try { return (java.lang.String) this.zYLInvocationHandler.invoke ( this, get2, o ); } catch (Exception e) { e.printStackTrace (); } return null; } public void adda() { Object[] o = {}; try { this.zYLInvocationHandler.invoke ( this, adda3, o ); return; } catch (Throwable e) { e.printStackTrace (); } } public java.lang.String geta() { Object[] o = {}; try { return (java.lang.String) this.zYLInvocationHandler.invoke ( this, geta4, o ); } catch (Exception e) { e.printStackTrace (); } return null; }
上面是要枚舉出來全部的方法的實現,很簡單都一個模樣,把實現交給handler去作就能夠了。至於怎麼實現靠handler,咱們動態生成的這個類只負責委託,不作任何事情。看到這裏你們必定急不可待的想知道這個類怎麼生成的了,我把我寫的源碼給你們貼出來看一下。
package meituan.zylproxy.util; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import meituan.zylproxy.test.i.Idto; import meituan.zylproxy.test.i.Idto2; public class ClassUtil { public static String mackProxyClass(Class<?> c) throws Exception{ if(!c.isInterface()){ throw new Exception("代理的類必須是接口"); } StringBuffer importsp = new StringBuffer(); importsp.append("import java.lang.reflect.Method;\n"); importsp.append("import meituan.zylproxy.handlder.ZylProxy;\n"); importsp.append("import meituan.zylproxy.handlder.ZYLInvocationHandler;\n"); importsp.append("import " +c.getName() + ";\n"); StringBuilder publicStaticMethods = new StringBuilder(); //public static Method add; StringBuilder publicMethods = new StringBuilder(); publicMethods.append("public ZYLInvocationHandler zYLInvocationHandler;\n"); StringBuilder constructorsp = new StringBuilder(); String interFaceName = c.getName().substring(c.getName().lastIndexOf(".")+1); constructorsp.append("public ").append("" + interFaceName + "Porxy"). append("(ZYLInvocationHandler zYLInvocationHandler) { " + "this.zYLInvocationHandler = zYLInvocationHandler;" + "}"); publicStaticMethods.append(" static { try { "); StringBuilder classsp = new StringBuilder(); classsp.append("public class").append(" " + interFaceName + "Porxy").append(" extends ZylProxy implements ").append(interFaceName).append("{"); StringBuilder allMethods = new StringBuilder(); Method[] Methods = c.getMethods(); int curr=0; for (Method m_:Methods) { curr++; publicMethods.append("public static Method ").append(m_.getName() + String.valueOf(curr)).append(";\n"); publicStaticMethods.append("").append(m_.getName() + String.valueOf(curr)).append("="); publicStaticMethods.append("Class.forName(\"" + c.getName() + "\")" + ".getMethod(\""+ m_.getName() +"\", "); StringBuilder sp =new StringBuilder(); StringBuilder spArgs = new StringBuilder(); spArgs.append("Object[] o ={"); //public sp.append(Modifier.toString(m_.getModifiers()).replace("abstract", "")).append(" "); //void | java.lang.String sp.append(m_.getReturnType().getName()).append(" "); //add()|get() sp.append(m_.getName().concat("(")); StringBuilder methodCLass = new StringBuilder(); if(m_.getParameterTypes().length>0){ Class<?>[] claszz = m_.getParameterTypes(); int methodOffset = 0; methodCLass.append("new Class[] { "); for (Class<?> c_ : claszz) { String paramStr = "obj" + String.valueOf(++methodOffset); spArgs.append(paramStr.concat(",")); sp.append(c_.getName().toString().concat(" ").concat(paramStr)).append(","); methodCLass.append("Class.forName(\"" + c_.getName()).append("\"),"); } sp = new StringBuilder(sp.substring(0, sp.length()-1)); spArgs = new StringBuilder(spArgs.substring(0, spArgs.length()-1)); methodCLass = new StringBuilder(methodCLass.substring(0, methodCLass.length()-1)); } if(methodCLass.length()>0){ methodCLass.append("}"); } else{ methodCLass.append("new Class[0]"); } sp.append("){\n"); spArgs.append("}"); sp.append(spArgs+";\n"); if(sp.toString().contains("void")){ sp.append("try {\n this.zYLInvocationHandler.invoke(this,").append(m_.getName() + String.valueOf(curr)).append(",").append("o);\n return;\n"); sp.append("} catch (Throwable e) {e.printStackTrace();}}"); } else{ sp.append("try {return " + "(" + m_.getReturnType().getName() + ")" + "this.zYLInvocationHandler.invoke(this,").append(m_.getName() + String.valueOf(curr)).append(",").append("o);\n"); sp.append("} catch (Exception e) {e.printStackTrace();} return null;"); } publicStaticMethods.append(methodCLass).append(");\n"); allMethods.append(sp); } publicStaticMethods.append("} catch(Exception e){}}"); classsp.append(publicMethods) .append(publicStaticMethods) .append(constructorsp).append(allMethods).append("}"); classsp.append("}"); importsp.append(classsp); return importsp.toString(); } public static String mackMultiProxyClass(Class<?>[] cs) throws Exception{ StringBuffer importsp = new StringBuffer(); importsp.append("import java.lang.reflect.Method;\n"); importsp.append("import meituan.zylproxy.handlder.ZylProxy;\n"); importsp.append("import meituan.zylproxy.handlder.ZYLInvocationHandler;\n"); StringBuilder publicStaticMethods = new StringBuilder(); publicStaticMethods.append(" static { try { "); //public static Method add; StringBuilder publicMethods = new StringBuilder(); publicMethods.append("public ZYLInvocationHandler zYLInvocationHandler;\n"); int curr=0; StringBuilder constructorsp = new StringBuilder(); String interFaceName = cs[0].getName().substring(cs[0].getName().lastIndexOf(".")+1); constructorsp.append("public ").append("" + interFaceName + "Porxy"). append("(ZYLInvocationHandler zYLInvocationHandler) { " + "this.zYLInvocationHandler = zYLInvocationHandler;" + "}"); StringBuilder allMethods = new StringBuilder(); StringBuilder classsp = new StringBuilder(); classsp.append("public class").append(" " + interFaceName + "Porxy").append(" extends ZylProxy implements "); for (Class<?> c:cs) { if(!c.isInterface()){ throw new Exception("代理的類必須是接口"); } classsp.append(c.getName().substring(c.getName().lastIndexOf(".")+1)).append(","); importsp.append("import " +c.getName() + ";\n"); Method[] Methods = c.getMethods(); for (Method m_:Methods) { curr++; publicMethods.append("public static Method ").append(m_.getName() + String.valueOf(curr)).append(";\n"); publicStaticMethods.append("").append(m_.getName() + String.valueOf(curr)).append("="); publicStaticMethods.append("Class.forName(\"" + c.getName() + "\")" + ".getMethod(\""+ m_.getName() +"\", "); StringBuilder sp =new StringBuilder(); StringBuilder spArgs = new StringBuilder(); spArgs.append("Object[] o ={"); //public sp.append(Modifier.toString(m_.getModifiers()).replace("abstract", "")).append(" "); //void | java.lang.String sp.append(m_.getReturnType().getName()).append(" "); //add()|get() sp.append(m_.getName().concat("(")); StringBuilder methodCLass = new StringBuilder(); if(m_.getParameterTypes().length>0){ Class<?>[] claszz = m_.getParameterTypes(); int methodOffset = 0; methodCLass.append("new Class[] { "); for (Class<?> c_ : claszz) { String paramStr = "obj" + String.valueOf(++methodOffset); spArgs.append(paramStr.concat(",")); sp.append(c_.getName().toString().concat(" ").concat(paramStr)).append(","); methodCLass.append("Class.forName(\"" + c_.getName()).append("\"),"); } sp = new StringBuilder(sp.substring(0, sp.length()-1)); spArgs = new StringBuilder(spArgs.substring(0, spArgs.length()-1)); methodCLass = new StringBuilder(methodCLass.substring(0, methodCLass.length()-1)); } if(methodCLass.length()>0){ methodCLass.append("}"); } else{ methodCLass.append("new Class[0]"); } sp.append("){\n"); spArgs.append("}"); sp.append(spArgs+";\n"); if(sp.toString().contains("void")){ sp.append("try {\n this.zYLInvocationHandler.invoke(this,").append(m_.getName() + String.valueOf(curr)).append(",").append("o);\n return;\n"); sp.append("} catch (Throwable e) {e.printStackTrace();}}"); } else{ sp.append("try {return " + "(" + m_.getReturnType().getName() + ")" + "this.zYLInvocationHandler.invoke(this,").append(m_.getName() + String.valueOf(curr)).append(",").append("o);\n"); sp.append("} catch (Exception e) {e.printStackTrace();} return null;}"); } publicStaticMethods.append(methodCLass).append(");\n"); allMethods.append(sp); } } classsp = new StringBuilder(classsp.substring(0, classsp.length()-1)).append("{"); publicStaticMethods.append("} catch(Exception e){}}"); classsp.append(publicMethods) .append(publicStaticMethods) .append(constructorsp).append(allMethods).append(""); classsp.append("}"); importsp.append(classsp); return importsp.toString(); } public static void main(String[] args) throws Exception { System.out.println(mackMultiProxyClass(new Class<?>[]{Idto.class})); } }
看起來很複雜,仔細看一下就看了那麼幾個事情,把一個接口class或者多個接口class變成純字符串的過程,一共兩個方法,一個是單接口的實現,很早以前寫的,第二個方法是多接口的實現支持多接口,只須要傳一個class對象就會生成代理類的字符串,這裏僅僅是字符串,須要編譯成class使用。那麼如何編譯成class呢。經過java中的工具類 JavaCompiler很簡單的就能夠生成了。咱們來看兩個工具類實現
package meituan.zylproxy; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.net.URI; import java.nio.CharBuffer; import java.nio.file.WatchEvent.Kind; import java.util.HashMap; import java.util.Map; import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; @SuppressWarnings("unchecked") final class MemoryJavaFileManager extends ForwardingJavaFileManager { private final static String EXT = ".java"; private Map<String, byte[]> classBytes; public MemoryJavaFileManager(JavaFileManager fileManager) { super(fileManager); classBytes = new HashMap<String, byte[]>(); } public Map<String, byte[]> getClassBytes() { return classBytes; } public void close() throws IOException { classBytes = new HashMap<String, byte[]>(); } public void flush() throws IOException { } private static class StringInputBuffer extends SimpleJavaFileObject { final String code; StringInputBuffer(String name, String code) { super(toURI(name), Kind.SOURCE); this.code = code; } public CharBuffer getCharContent(boolean ignoreEncodingErrors) { return CharBuffer.wrap(code); } public Reader openReader() { return new StringReader(code); } } private class ClassOutputBuffer extends SimpleJavaFileObject { private String name; ClassOutputBuffer(String name) { super(toURI(name), Kind.CLASS); this.name = name; } public OutputStream openOutputStream() { return new FilterOutputStream(new ByteArrayOutputStream()) { public void close() throws IOException { out.close(); ByteArrayOutputStream bos = (ByteArrayOutputStream) out; classBytes.put(name, bos.toByteArray()); } }; } } public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { if (kind == JavaFileObject.Kind.CLASS) { return new ClassOutputBuffer(className); } else { return super.getJavaFileForOutput(location, className, kind, sibling); } } static JavaFileObject makeStringSource(String name, String code) { return new StringInputBuffer(name, code); } static URI toURI(String name) { File file = new File(name); if (file.exists()) { return file.toURI(); } else { try { final StringBuilder newUri = new StringBuilder(); newUri.append("mfm:///"); newUri.append(name.replace('.', '/')); if (name.endsWith(EXT)) newUri.replace(newUri.length() - EXT.length(), newUri.length(), EXT); return URI.create(newUri.toString()); } catch (Exception exp) { return URI.create("mfm:///com/sun/script/java/java_source"); } } } }
package meituan.zylproxy; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class DynamicLoader { public static Map<String, byte[]> compile(String javaSrc) { Pattern pattern = Pattern.compile("public\\s+class\\s+(\\w+)"); Matcher matcher = pattern.matcher(javaSrc); if (matcher.find()) return compile(matcher.group(1) + ".java", javaSrc); return null; } public static Map<String, byte[]> compile(String javaName, String javaSrc) { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null); try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) { JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc); JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject)); if (task.call()) return manager.getClassBytes(); } catch (IOException e) { e.printStackTrace(); } return null; } public static class MemoryClassLoader extends URLClassLoader { Map<String, byte[]> classBytes = new HashMap<String, byte[]>(); public MemoryClassLoader(Map<String, byte[]> classBytes) { super(new URL[0], MemoryClassLoader.class.getClassLoader()); this.classBytes.putAll(classBytes); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] buf = classBytes.get(name); if (buf == null) { return super.findClass(name); } classBytes.remove(name); return defineClass(name, buf, 0, buf.length); } } }
經過DynamicLoader的compile方法能夠把純字符串的str轉成byte[]數組,有了byte[]數組就能夠很方便的獲取到class對象了,自定義一個MemoryClassLoader經過defineClass方法來獲取到class對象。這樣基本全部的事情都作完了。下面咱們寫一個工廠類來獲取代理類。
package meituan.zylproxy.util; import java.util.Map; import meituan.zylproxy.DynamicLoader; import meituan.zylproxy.handlder.ZYLInvocationHandler; public class PorxyFactory { //單interface的時候用 public static Object newProxyInstance(Class<?> c,ZYLInvocationHandler h) throws Exception{ String classStr = ClassUtil.mackProxyClass(c); Map<String, byte[]> m = DynamicLoader.compile(classStr); DynamicLoader.MemoryClassLoader classLoader = new DynamicLoader.MemoryClassLoader(m); Class<?> proxy =classLoader.loadClass(m.keySet().toArray(new String[0])[0]); return proxy.getConstructor(ZYLInvocationHandler.class).newInstance(h); } //多interface的時候用 public static Object newProxyInstancewWithMultiClass(Class<?>[] c,ZYLInvocationHandler h) throws Exception{ String classStr = ClassUtil.mackMultiProxyClass(c); System.out.println (classStr); Map<String, byte[]> m = DynamicLoader.compile(classStr); DynamicLoader.MemoryClassLoader classLoader = new DynamicLoader.MemoryClassLoader(m); Class<?> proxy =classLoader.loadClass(m.keySet().toArray(new String[0])[0]); return proxy.getConstructor(ZYLInvocationHandler.class).newInstance(h); } }
最後一步咱們測試一下結果吧,寫一個測試類
package meituan.zylproxy.test; import meituan.zylproxy.handlder.Hander; import meituan.zylproxy.test.i.Idto; import meituan.zylproxy.test.i.impl.DtoImpl; import meituan.zylproxy.util.PorxyFactory; public class ZylPorxyTest { public static void main(String[] args) throws Exception { Idto d = (Idto) PorxyFactory.newProxyInstancewWithMultiClass(DtoImpl.class.getInterfaces(), new Hander(new DtoImpl())); d.add(); } }
很簡單,第一個參數是全部的接口,第二個是handler實現。最後咱們看看結果。
大功告成。
做者簡介:就任美團外賣業務架構組,有更多的更多的源碼交流,請加羣825199617交流,spring源碼,spring mvc源碼,手寫企業級高可用rpc框架,java企業級熱部署解決方案等等更多精彩源碼,等你來。