細讀Spring原始碼(三)---深度剖析動態代理底層原理
往期回顧:
動態代理傳送門(兩篇比較好的文章,搬運過來):
上一篇文章梳理了Spring中用到的設計模式,其中就用程式碼演示了JDK和CGLIB兩種動態代理的實現方式,本文會稍微跟一下這兩種代理的原始碼,為後面的AOP做準備。在介紹這兩種代理之前先看一下下面這段程式碼:
1 public class ShowReflect { 2 3 public static voidmain(String[] args) { 4 Face faceInstance = getFaceInstance(); 5 faceInstance.setEye("hello"); 6 faceInstance.setSize(1); 7 System.out.println(faceInstance); 8 } 9 10 public static Face getFaceInstance() { 11 Class<Face> faceClass = Face.class; 12 Constructor<?>[] constructors = faceClass.getConstructors(); 13 Face face = null; 14 for (Constructor con : constructors) { 15 Parameter[] parameters = con.getParameters(); 16 if (parameters == null || parameters.length == 0) { 17 try{ 18 face = (Face) con.newInstance(); 19 } catch (InstantiationException e) { 20 e.printStackTrace(); 21 System.out.println(e); 22 } catch (IllegalAccessException e) { 23 e.printStackTrace(); 24 System.out.println(e); 25 } catch (InvocationTargetException e) { 26 e.printStackTrace(); 27 System.out.println(e); 28 } 29 } 30 } 31 return face; 32 } 33 }
猜猜最後會列印什麼呢?沒錯,是Face{eye='hello',size='1'},其實getFaceIntance()方法的效果就等同於Face face = new Face();,下面來看下解析:
上面的例子其實就是通過反射的方式建立了一個Face類的例項,然後就可以對它進行各種操作了,為什麼要先提反射呢?因為它是動態代理的底層實現原理,不管是JDK動態代理,還是CGLIB動態代理,在動態建立代理物件的過程中,都是通過反射機制實現。
上一篇文章已經通過兩個demo實現了,下面來看看這兩種實現方式的底層原始碼:
一、JDK動態代理
下面是建立代理物件的過程
1 Class[] interfaces = {UserService.class}; 2 UserService userService = (UserService) Proxy.newProxyInstance(UserJdkProxy.class.getClassLoader(), interfaces, new UserJdkProxy(new UserServiceImpl()));
從上面第2行程式碼看出,jdk通過Proxy類的靜態方法newProxyInstance建立了一個代理物件newProxyInstance一探究竟
1 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { 2 Objects.requireNonNull(h); 3 //1.克隆一個目標介面的Class物件,該介面就是被代理物件的介面 4 final Class<?>[] intfs = interfaces.clone(); 5 //2.檢查並設定目標介面Class物件intfs的訪問許可權 6 final SecurityManager sm = System.getSecurityManager(); 7 if (sm != null) { 8 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 9 } 10 /* 11 * Look up or generate the designated proxy class. 12 * 3.獲取一個代理類Class物件,這個代理類就是要動態建立的目標,返回的是一個名稱為com.sun.proxy.$Proxy0的Class物件 13 */ 14 Class<?> cl = getProxyClass0(loader, intfs); 15 16 /* 17 * Invoke its constructor with the designated invocation handler. 18 * 4.用給定的invocation handler回撥它的構造器 19 */ 20 try { 21 //4.1 檢查並設定代理類的Class物件cl的訪問許可權 22 if (sm != null) { 23 checkNewProxyPermission(Reflection.getCallerClass(), cl); 24 } 25 //4.2 獲取代理類的構造器 26 final Constructor<?> cons = cl.getConstructor(constructorParams); 27 final InvocationHandler ih = h; 28 if (!Modifier.isPublic(cl.getModifiers())) { 29 AccessController.doPrivileged(new PrivilegedAction<Void>() { 30 public Void run() { 31 cons.setAccessible(true); 32 return null; 33 } 34 }); 35 } 36 //4.2 通過構造器建立一個例項 37 return cons.newInstance(new Object[]{h}); 38 } catch (IllegalAccessException | InstantiationException e) { 39 throw new InternalError(e.toString(), e); 40 } catch (InvocationTargetException e) { 41 Throwable t = e.getCause(); 42 if (t instanceof RuntimeException) { 43 throw (RuntimeException) t; 44 } else { 45 throw new InternalError(t.toString(), t); 46 } 47 } catch (NoSuchMethodException e) { 48 throw new InternalError(e.toString(), e); 49 } 50 }
這一切的一切就是建立一個代理物件的底層實現原理,其步驟總結下來就是:
1、克隆目標介面的Class物件intfs
2、以目標介面的Class物件intfs為引數獲取代理類的Class物件cl(備註:這裡要這樣實現,我想是因為代理類中維護了被代理的目標物件,所以需要在其Class物件中定義這個欄位,這個過程可以理解為動態地生成一個Class物件)
3、通過代理類的Class物件cl得到其構造器
4、通過構造器建立例項
上面的第三步和第四步有沒有覺得眼熟呢?沒錯,它就是反射!
奔著既知其一又知其二的心態,接下來,我們逐步跟進,看一下上面的第14行,也就是第三步獲取代理類的Class物件方法如下:
1 /** 2 * Generate a proxy class. Must call the checkProxyAccess method 3 * to perform permission checks before calling this. 4 * 生成一個代理類。在呼叫該方法之前,必須呼叫checkProxyAccess方法執行許可權檢查 5 */ 6 private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { 7 if (interfaces.length > 65535) { 8 throw new IllegalArgumentException("interface limit exceeded"); 9 } 10 // If the proxy class defined by the given loader implementing the given interfaces exists, this will simply return the cached copy; 11 // otherwise, it will create the proxy class via the ProxyClassFactory 12 //如果一個代理類已經被實現了給定介面的給定類載入器所定義,那麼久直接返回該快取的副本,否則將會通過代理類工廠ProxyClassFactory建立一個代理物件 13 return proxyClassCache.get(loader, interfaces); 14 }
這個方法有趣的一點是,方法名上居然有個數字0,我們知道0在計算機中就是第一個的意思,再看原始碼註釋第10行,說如果一個代理類已經存在,則返回其快取,否則建立一個新的,我想這就能解釋為什麼方法名中要帶0了吧,即永遠取已經存在的,也就是第一個了。
下面繼續跟蹤第13行,因為呼叫鏈比較長,我摘出了下面這段精髓程式碼,來一睹代理類的Class物件建立誕生過程:
首先,會在Proxy的ProxyClassFactory方法中呼叫proxyClassCache.get方法
然後,在WeakCache類的get方法中呼叫subKeyFactory.apply(key, parameter),其中的subKeyFactory是一個型別為BiFunction的介面,該介面有一個實現類ProxyClassFactory,是Proxy的一個靜態內部類
接著,在ProxyClassFactory中實現apply介面,其實get方法中最後呼叫的還是ProxyClassFactory.apply()方法
下面來看下這個神奇的匿名內部的原始碼:
1 /** 2 * A factory function that generates, defines and returns the proxy class given 3 * the ClassLoader and array of interfaces. 4 * 這是Proxy的一個靜態內部類,它是一個工廠,具備這些功能:生成、定義和返回給定類載入器和一組介面對應代理類 5 */ 6 private static final class ProxyClassFactory 7 implements BiFunction<ClassLoader, Class<?>[], Class<?>> { 8 // prefix for all proxy class names 9 //代理類的名稱的字首 10 private static final String proxyClassNamePrefix = "$Proxy"; 11 12 // next number to use for generation of unique proxy class names 13 //生成下一個唯一代理類名稱的數字 14 private static final AtomicLong nextUniqueNumber = new AtomicLong(); 15 16 @Override 17 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { 18 19 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); 20 for (Class<?> intf : interfaces) { 21 /* 22 * Verify that the class loader resolves the name of this 23 * interface to the same Class object. 24 * 驗證類載入器解析的與這個介面同名的Class物件:其實就是再校驗被代理介面是否是該類載入器所載入的 25 */ 26 Class<?> interfaceClass = null; 27 try { 28 interfaceClass = Class.forName(intf.getName(), false, loader); 29 } catch (ClassNotFoundException e) { 30 } 31 if (interfaceClass != intf) { 32 throw new IllegalArgumentException( 33 intf + " is not visible from class loader"); 34 } 35 /* 36 * Verify that the Class object actually represents an 37 * interface. 38 * 驗證Class物件的確是一個介面,這就是為什麼jdk動態代理需要實現介面的原因!!! 39 */ 40 if (!interfaceClass.isInterface()) { 41 throw new IllegalArgumentException( 42 interfaceClass.getName() + " is not an interface"); 43 } 44 /* 45 * Verify that this interface is not a duplicate. 46 * //驗證這個介面不是重複的 47 */ 48 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { 49 throw new IllegalArgumentException( 50 "repeated interface: " + interfaceClass.getName()); 51 } 52 } 53 54 String proxyPkg = null; // package to define proxy class in 55 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; 56 57 /* 58 * Record the package of a non-public proxy interface so that the 59 * proxy class will be defined in the same package. Verify that 60 * all non-public proxy interfaces are in the same package. 61 * 定義一個包名,如果被代理類的介面是非公開的,則所有代理類都會生成到這個包下:反正這裡就是生成一個存放含有非公開方法的代理類Class檔案的包 62 */ 63 for (Class<?> intf : interfaces) { 64 int flags = intf.getModifiers(); 65 if (!Modifier.isPublic(flags)) { 66 accessFlags = Modifier.FINAL; 67 String name = intf.getName(); 68 int n = name.lastIndexOf('.'); 69 String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); 70 if (proxyPkg == null) { 71 proxyPkg = pkg; 72 } else if (!pkg.equals(proxyPkg)) { 73 throw new IllegalArgumentException( 74 "non-public interfaces from different packages"); 75 } 76 } 77 } 78 79 if (proxyPkg == null) { 80 // if no non-public proxy interfaces, use com.sun.proxy package 81 //如果要代理的類中不包含非公開方法,則使用com.sun.proxy作為代理類的包名 82 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; 83 } 84 85 /* 86 * Choose a name for the proxy class to generate. 87 * 為代理類Class生成一個名稱:包名+$Proxy+數字(自動增長) 88 */ 89 long num = nextUniqueNumber.getAndIncrement(); 90 String proxyName = proxyPkg + proxyClassNamePrefix + num; 91 92 /* 93 * Generate the specified proxy class. 94 * 生成指定的代理類:一個自己陣列 95 */ 96 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( 97 proxyName, interfaces, accessFlags); 98 try { 99 //通過生成的位元組陣列定義一個Class物件:最後會在com.sun.proxy包下生成一個名為$Proxy+num的Class檔案,如:com.sun.proxy.$Proxy0.class 100 return defineClass0(loader, proxyName, 101 proxyClassFile, 0, proxyClassFile.length); 102 } catch (ClassFormatError e) { 103 /* 104 * A ClassFormatError here means that (barring bugs in the 105 * proxy class generation code) there was some other 106 * invalid aspect of the arguments supplied to the proxy 107 * class creation (such as virtual machine limitations 108 * exceeded). 109 */ 110 throw new IllegalArgumentException(e.toString()); 111 } 112 } 113 }
看了原始碼,才知道通常所說的$Proxy0是這樣來的,這個類其實就是一個工廠,它會定義並生成一個代理類的Class物件,生成過程會根據被代理介面中的方法是否是public,來確定包名,還會通過$Proxy+數字的形式給代理類命名。下面根據第96行,來看看這個代理類是怎麼生成的?
1 /** 2 * Generate a proxy class given a name and a list of proxy interfaces. 3 * 根據給定的名稱和一組代理介面生成一個代理類 4 * 5 * @param name the class name of the proxy class 代理類的名稱 6 * @param interfaces proxy interfaces 代理介面 7 * @param accessFlags access flags of the proxy class 代理類的訪問標識 8 */ 9 public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) { 10 //1、建立一個代理生成器物件例項gen 11 ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); 12 /*2、呼叫例項gen的generateClassFile方法生成Class位元組碼陣列,該方法內完成了如下事件: 13 第一步:新增hashCode、equals、toString等方法 14 第二步:新增代理介面中的方法,按照介面的順序,先新增的介面優先 15 第三步:驗證代理方法的返回型別 16 第四步:為該類中所有屬性和方法轉配FiledInfo和MethodInfo的結構體 17 第五步:開始寫入DataOutputStream,即構建一個class物件 18 第六步:返回位元組陣列 19 */ 20 final byte[] classFile = gen.generateClassFile(); 21 //3、如果需要儲存該檔案,則在指定路徑下生成.class的檔案 22 if (saveGeneratedFiles) { 23 java.security.AccessController.doPrivileged( 24 new java.security.PrivilegedAction<Void>() { 25 public Void run() { 26 try { 27 int i = name.lastIndexOf('.'); 28 Path path; 29 if (i > 0) { 30 Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar)); 31 Files.createDirectories(dir); 32 path = dir.resolve(name.substring(i + 1, name.length()) + ".class"); 33 } else { 34 path = Paths.get(name + ".class"); 35 } 36 Files.write(path, classFile); 37 return null; 38 } catch (IOException e) { 39 throw new InternalError( 40 "I/O exception saving generated file: " + e); 41 } 42 } 43 }); 44 } 45 //4、返回位元組陣列 46 return classFile; 47 }
上面程式碼中我都添加了註釋,其實核心的程式碼還不在這裡,而是第20行,返回一個final修飾的位元組陣列,它裡面的主要步驟我已經新增到註釋中,為了一探究竟,我還是決定把它的程式碼附上,保證看到它的你會直呼牛逼!!!
1 /** 2 * Generate a class file for the proxy class. This method drives the 3 * class file generation process. 4 * 為代理物件生成一個class檔案,這個方法會驅動class檔案生成程式 5 */ 6 private byte[] generateClassFile() { 7 8 /* ============================================================ 9 * Step 1: Assemble ProxyMethod objects for all methods to 10 * generate proxy dispatching code for. 11 */ 12 13 /* 14 * Record that proxy methods are needed for the hashCode, equals, 15 * and toString methods of java.lang.Object. This is done before 16 * the methods from the proxy interfaces so that the methods from 17 * java.lang.Object take precedence over duplicate methods in the 18 * proxy interfaces. 19 */ 20 //第一步:新增hashCode、equals、toString等方法 21 //第二步:新增代理介面中的方法,按照介面的順序,先新增的介面優先 22 //第三步:驗證代理方法的返回型別 23 //第四步:為該類中所有屬性和方法轉配FiledInfo和MethodInfo的結構體 24 //第五步:開始寫入DataOutputStream,即構建一個class物件 25 //第六步:返回位元組陣列 26 addProxyMethod(hashCodeMethod, Object.class); 27 addProxyMethod(equalsMethod, Object.class); 28 addProxyMethod(toStringMethod, Object.class); 29 30 /* 31 * Now record all of the methods from the proxy interfaces, giving 32 * earlier interfaces precedence over later ones with duplicate 33 * methods. 34 */ 35 //第二步:新增代理介面中的方法,按照介面的順序,先新增的介面優先 36 // 這裡其實就是將所有被代理介面中的方法都封裝成一個ProxyMethod物件,該物件中定義了方法名、引數型別、返回型別、異常型別、方法來源、方法中的區域性變數名等 37 for (Class<?> intf : interfaces) { 38 for (Method m : intf.getMethods()) { 39 addProxyMethod(m, intf); 40 } 41 } 42 43 /* 44 * For each set of proxy methods with the same signature, 45 * verify that the methods' return types are compatible. 46 */ 47 //第三步:驗證每一個方法中的返回型別 48 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 49 checkReturnTypes(sigmethods); 50 } 51 52 /* ============================================================ 53 * Step 2: Assemble FieldInfo and MethodInfo structs for all of 54 * fields and methods in the class we are generating. 55 */ 56 //第四步:為該類中所有屬性和方法轉配FiledInfo和MethodInfo的結構體 57 //這裡會解析每一個代理方法生成的ProxyMethods物件,最後生成兩個集合分別存放方法和引數 58 try { 59 methods.add(generateConstructor()); 60 61 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { 62 for (ProxyMethod pm : sigmethods) { 63 64 // add static field for method's Method object 65 fields.add(new FieldInfo(pm.methodFieldName, 66 "Ljava/lang/reflect/Method;", 67 ACC_PRIVATE | ACC_STATIC)); 68 69 // generate code for proxy method and add it 70 methods.add(pm.generateMethod()); 71 } 72 } 73 74 methods.add(generateStaticInitializer()); 75 76 } catch (IOException e) { 77 throw new InternalError("unexpected I/O Exception", e); 78 } 79 80 if (methods.size() > 65535) { 81 throw new IllegalArgumentException("method limit exceeded"); 82 } 83 if (fields.size() > 65535) { 84 throw new IllegalArgumentException("field limit exceeded"); 85 } 86 87 /* ============================================================ 88 * Step 3: Write the final class file. 89 */ 90 91 /* 92 * Make sure that constant pool indexes are reserved for the 93 * following items before starting to write the final class file. 94 */ 95 //第五步:確保在開始往final型別的Class檔案中寫之前,className、父類className和介面等有足夠的常量池 96 cp.getClass(dotToSlash(className)); 97 cp.getClass(superclassName); 98 for (Class<?> intf : interfaces) { 99 cp.getClass(dotToSlash(intf.getName())); 100 } 101 102 /* 103 * Disallow new constant pool additions beyond this point, since 104 * we are about to write the final constant pool table. 105 */ 106 cp.setReadOnly(); 107 108 //第五步:開始寫入DataOutputStream,即構建一個class物件 109 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 110 DataOutputStream dout = new DataOutputStream(bout); 111 112 try { 113 /* 114 * Write all the items of the "ClassFile" structure. 115 * See JVMS section 4.1. 116 * 核心:0xCAFEBABE是不是很熟?沒錯,這就是動態生成一個class物件 117 */ 118 // u4 magic; 119 dout.writeInt(0xCAFEBABE); 120 // u2 minor_version; 121 dout.writeShort(CLASSFILE_MINOR_VERSION); 122 // u2 major_version; 123 dout.writeShort(CLASSFILE_MAJOR_VERSION); 124 125 cp.write(dout); // (write constant pool) 126 127 // u2 access_flags; 128 dout.writeShort(accessFlags); 129 // u2 this_class; 130 dout.writeShort(cp.getClass(dotToSlash(className))); 131 // u2 super_class; 132 dout.writeShort(cp.getClass(superclassName)); 133 134 // u2 interfaces_count; 135 dout.writeShort(interfaces.length); 136 // u2 interfaces[interfaces_count]; 137 for (Class<?> intf : interfaces) { 138 dout.writeShort(cp.getClass( 139 dotToSlash(intf.getName()))); 140 } 141 142 // u2 fields_count; 143 dout.writeShort(fields.size()); 144 // field_info fields[fields_count]; 145 for (FieldInfo f : fields) { 146 f.write(dout); 147 } 148 149 // u2 methods_count; 150 dout.writeShort(methods.size()); 151 // method_info methods[methods_count]; 152 for (MethodInfo m : methods) { 153 m.write(dout); 154 } 155 156 // u2 attributes_count; 157 dout.writeShort(0); // (no ClassFile attributes for proxy classes) 158 159 } catch (IOException e) { 160 throw new InternalError("unexpected I/O Exception", e); 161 } 162 //第六步:返回位元組陣列 163 return bout.toByteArray(); 164 }
上面的第26、27、28、39行的addProxyMethod方方法就是將一個方法封裝成一個ProxyMethod的物件,這就體現了Java中一切皆物件的宗旨,這些還不夠,再看第65~74行,由將ProxyMethod中的方法定義和方法引數定義封裝成MthodInfo和FidleInfo物件,看到這裡才能領悟到什麼叫“一切皆物件”了。再來看119~157行程式碼,看到這裡是不是滿臉震驚,沒錯我也是!這是啥?這是在動態地生成位元組碼呀!看到魔數0xCAFEBABE就真相大白了,jdk動態代理的靈魂就是動態地生成一個class檔案,該檔案中包含了對被代理物件中方法的呼叫,這才是java的精髓所在啊!!!
至此,jdk動態代理的原始碼就跟完了,下面總結一下吧:
1、jdk動態代理是基於介面的代理,被代理的必須是個介面或實現了某個介面,因為底層會判斷目標物件是否是介面型別
2、通過克隆的方式得到目標介面的class物件
3、通過動態生成位元組碼檔案的方式生成代理物件的Class物件
4、通過反射獲取代理物件的構造器,並通過構造器生成代理物件的例項
二、CGLIB動態代理
CGLIB是為一個類進行代理的,那麼先看下它是如何生成一個代理類的,下面程式碼是建立一個代理類:
1 public T getProxy(Class<T> targetClass) { 2 Enhancer enhancer = new Enhancer(); 3 return (T) enhancer.create(targetClass, this); 4 }
下面照例一步一步跟進,先跟new Enhancer()方法:
1 /** 2 * Create a new <code>Enhancer</code>. A new <code>Enhancer</code> 3 * object should be used for each generated object, and should not 4 * be shared across threads. To create additional instances of a 5 * generated class, use the <code>Factory</code> interface. 6 * 該方法用來建立一個Enhancer。必須保證每次生成物件時都會建立一個新的Enhancer,而且它不能被多個執行緒共享 7 * 如果要建立一個額外的生成例項,需要使用Factory介面 8 * @see Factory 9 */ 10 public Enhancer() { 11 super(SOURCE); 12 }
這個建構函式中呼叫了父類AbstractClassGenerator的建構函式,建立了一個Source物件,AbstractClassGenerator顧名思義就是抽象類生成器。下面再看下一步,enhancer.create(targetClass, this);
1 /** 2 * Generate a new class if necessary and uses the specified 3 * callbacks (if any) to create a new object instance. 4 * Uses the no-arg constructor of the superclass. 5 * 如果必要的話生成一個新的class,並使用指定的回撥函式(如果存在)建立一個新的例項,使用父類的無參建構函式 6 * @return a new instance 7 */ 8 public Object create() { 9 classOnly = false; 10 argumentTypes = null; 11 return createHelper(); 12 }
主要來看第11行中的createHelper方法:
1 private Object createHelper() { 2 //預檢查:如果沒有指定過濾器,且回撥物件有多個,就拋異常 3 preValidate(); 4 //生成一個key物件 5 Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null, 6 ReflectUtils.getNames(interfaces), 7 filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter), 8 callbackTypes, 9 useFactory, 10 interceptDuringConstruction, 11 serialVersionUID); 12 this.currentKey = key; 13 //呼叫父類的建立create方法,根據key建立代理類 14 Object result = super.create(key); 15 return result; 16 }
通過debug發現,上面的測試程式碼會生成下面這樣一串key:
裡面最主要的就是被代理的類和當前代理類的型別,即MethodInterceptor介面
繼續追蹤super.create(key);這行程式碼的具體實現:
1 //定義一個volatile修飾的快取變數CACHE 2 private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<ClassLoader, ClassLoaderData>(); 3 4 protected Object create(Object key) { 5 try { 6 //獲取類載入器 7 ClassLoader loader = getClassLoader(); 8 Map<ClassLoader, ClassLoaderData> cache = CACHE; 9 //從快取中獲取類載入器的資料 10 ClassLoaderData data = cache.get(loader); 11 //如果類載入器不存在,則新建立 12 if (data == null) { 13 //看到沒?基於雙重檢查機制的單例模式啊!!!! 14 synchronized (AbstractClassGenerator.class) { 15 //注意注意:嚴謹的是這個地方又重新進行了一次set和get操作,保證CACHE沒有被別的執行緒修改,這大概才是最好的Double Check吧!!! 16 cache = CACHE; 17 data = cache.get(loader); 18 //進行第二次檢查 19 if (data == null) { 20 //當第二次檢查還不存在的時候才建立新的類載入器 21 Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache); 22 //該方法中使類載入器指向一個弱引用WeakReference 23 data = new ClassLoaderData(loader); 24 newCache.put(loader, data); 25 CACHE = newCache; 26 } 27 } 28 } 29 this.key = key; 30 //判斷是否開啟快取配置,即cglib.useCache=true 31 //如果開啟快取,就從快取中獲取,如果沒有開啟,就直接生成並返回 32 Object obj = data.get(this, getUseCache()); 33 //如果返回的是一個Class,則呼叫firstInstance 34 if (obj instanceof Class) { 35 //建立第一個例項:如果返回的是一個Class 36 return firstInstance((Class) obj); 37 } 38 //否則呼叫nextInstance 39 return nextInstance(obj); 40 } catch (RuntimeException | Error ex) { 41 throw ex; 42 } catch (Exception ex) { 43 throw new CodeGenerationException(ex); 44 } 45 }
這裡12~28行獲取類載入器資料的地方使用了一個經典的基於雙重檢查的單例模式!平復一下心情,繼續看兩個return後面的方法,第一個是第一次執行,第二個是非第一次執行:
1 protected Object firstInstance(Class type) throws Exception { 2 //如果只是建立一個類,則直接返回 3 if (classOnly) { 4 return type; 5 } 6 else { 7 //否則通過反射建立 8 return createUsingReflection(type); 9 } 10 } 11 12 //在例項已經存在的情況下建立 13 protected Object nextInstance(Object instance) { 14 EnhancerFactoryData data = (EnhancerFactoryData) instance; 15 16 //同樣,如果只是返回Class物件,則呼叫例項的generatedClass返回 17 if (classOnly) { 18 return data.generatedClass; 19 } 20 //否則建立新的例項 21 Class[] argumentTypes = this.argumentTypes; 22 Object[] arguments = this.arguments; 23 if (argumentTypes == null) { 24 argumentTypes = Constants.EMPTY_CLASS_ARRAY; 25 arguments = null; 26 } 27 return data.newInstance(argumentTypes, arguments, callbacks); 28 }
其實debug會發現,每次都會執行到nextInstance方法中,因為我們是要獲取一個代理物件的例項,firstInstance中是通過反射實現,那麼再看nextInstance方法中是怎麼實現的呢?
1 public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) { 2 setThreadCallbacks(callbacks); 3 try { 4 // Explicit reference equality is added here just in case Arrays.equals does not have one 5 if (primaryConstructorArgTypes == argumentTypes || 6 Arrays.equals(primaryConstructorArgTypes, argumentTypes)) { 7 // If we have relevant Constructor instance at hand, just call it 8 // This skips "get constructors" machinery 9 return ReflectUtils.newInstance(primaryConstructor, arguments); 10 } 11 // Take a slow path if observing unexpected argument types 12 return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments); 13 } 14 finally { 15 // clear thread callbacks to allow them to be gc'd 16 setThreadCallbacks(null); 17 } 18 19 }
看到沒,底層還是通過反射獲取到建構函式,再通過建構函式的newInstance來建立物件的。
至此,兩種動態代理的原始碼分析就差不多了,從JDK原始碼到Spring原始碼,總會看到很多有意思的寫法和最佳實踐,這大概就是看原始碼不覺得枯燥的地方了吧,哈哈~~下面還是來總結一下本章的內容:
1、JDK動態代理是基於介面的代理,是java語言的特性,代理類需要實現InvocationHandler介面,被代理的是一個介面
2、CGLIB動態代理是基於類的代理,是Spring與第三方整合實現,代理類需要實現MethodInterceptor介面,被代理的是一個類
3、代理物件的生成,底層都使用了反射方式,先獲取建構函式,在通過建構函式建立例項
以上就是本章全部內容~~
本文來自部落格園,作者:bug改了我,轉載請註明原文連結:https://www.cnblogs.com/hellowhy/p/15627320.html