Previous review:
-
Read Spring source code (I) -- overview of refresh() method
-
Read the Spring source code (II) -- about the design patterns used in Spring
Dynamic proxy portal (two good articles, moving over):
What is the role of Java dynamic proxy?
Implementation and principle of java Dynamic agent
The last article combed the design patterns used in Spring, and demonstrated the implementation methods of JDK and CGLIB dynamic agents with code. This article will follow the source code of these two agents to prepare for the following AOP. Before introducing these two agents, let's take a look at the following code:
1 public class ShowReflect { 2 3 public static void main(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 }
Guess what will be printed in the end? Yes, it's Face{eye='hello',size='1'}. In fact, the effect of getfaceinlet() method is equivalent to Face face = new Face();, Let's take a look at the analysis:
The above example actually creates an instance of Face class through reflection, and then you can perform various operations on it. Why mention reflection first? Because it is the underlying implementation principle of dynamic agent, both JDK dynamic agent and CGLIB dynamic agent are implemented through reflection mechanism in the process of dynamically creating agent objects.
Last article It has been implemented through two demo s. Let's take a look at the underlying source code of these two implementations:
1, JDK dynamic agent
The following is the process of creating a proxy object
1 Class[] interfaces = {UserService.class}; 2 UserService userService = (UserService) Proxy.newProxyInstance(UserJdkProxy.class.getClassLoader(), interfaces, new UserJdkProxy(new UserServiceImpl()));
From the code in line 2 above, jdk creates a Proxy object newProxyInstance through the static method newProxyInstance of the Proxy class to find out
1 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { 2 Objects.requireNonNull(h); 3 //1.Clone a target interface Class Object, which is the interface of the proxied object 4 final Class<?>[] intfs = interfaces.clone(); 5 //2.Check and set the target interface Class object intfs Access rights for 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.Get a proxy Class object, which is the target to be created dynamically, and return a Class object named com.sun.proxy.$Proxy0 13 */ 14 Class<?> cl = getProxyClass0(loader, intfs); 15 16 /* 17 * Invoke its constructor with the designated invocation handler. 18 * 4.Callback its constructor with the given invocation handler 19 */ 20 try { 21 //4.1 Check and set the of the proxy class Class object cl Access rights for 22 if (sm != null) { 23 checkNewProxyPermission(Reflection.getCallerClass(), cl); 24 } 25 //4.2 Gets the constructor of the proxy class 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 Create an instance through the constructor 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 }
All this is the underlying implementation principle of creating a proxy object. The steps are summarized as follows:
1. Clone the Class object intfs of the target interface
2. Take the Class object intfs of the target interface as the parameter to obtain the Class object cl of the proxy Class (Note: to implement this here, I think it is because the proxy Class maintains the proxy target object, so this field needs to be defined in its Class object. This process can be understood as dynamically generating a Class object)
3. Get its constructor through the Class object cl of the proxy Class
4. Create instance through constructor
Do you feel familiar with the third and fourth steps above? Yes, it's reflection!
With the mentality of knowing both one and the other, let's follow up step by step and take a look at line 14 above, that is, the third step to obtain the Class object of the proxy Class. The method is as follows:
1 /** 2 * Generate a proxy class. Must call the checkProxyAccess method 3 * to perform permission checks before calling this. 4 * Generate a proxy class. Before calling this method, you must call the checkProxyAccess method to perform a permission check 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 //If a proxy class has been defined by a given class loader that implements a given interface, a copy of the cache will be returned directly, otherwise it will pass through the proxy class factory ProxyClassFactory Create a proxy object 13 return proxyClassCache.get(loader, interfaces); 14 }
The interesting thing about this method is that there is a number 0 in the method name. We know that 0 is the first meaning in the computer. Look at line 10 of the source code comment, which says that if a proxy class already exists, its cache will be returned, otherwise a new one will be created. I think this can explain why there is a 0 in the method name, that is, the existing one will always be taken, That's the first one.
Let's continue to trace line 13. Because the call chain is long, I picked up the following quintessence code to see the creation and birth process of Class object of proxy Class:
First, the proxyClassCache.get method will be called in the ProxyClassFactory method of Proxy.
Then, subKeyFactory.apply(key, parameter) is called in the get method of the WeakCache class, where subKeyFactory is an interface of type BiFunction, which has an implementation class ProxyClassFactory, which is a static internal class of Proxy.
Next, the apply interface is implemented in ProxyClassFactory. In fact, the last call in the get method is the ProxyClassFactory.apply() method.
Let's take a look at this magical anonymous internal source code:
1 /** 2 * A factory function that generates, defines and returns the proxy class given 3 * the ClassLoader and array of interfaces. 4 * This is a static internal class of Proxy. It is a factory with these functions: generating, defining and returning a given class loader and a set of Proxy classes corresponding to interfaces 5 */ 6 private static final class ProxyClassFactory 7 implements BiFunction<ClassLoader, Class<?>[], Class<?>> { 8 // prefix for all proxy class names 9 //Prefix to the name of the proxy class 10 private static final String proxyClassNamePrefix = "$Proxy"; 11 12 // next number to use for generation of unique proxy class names 13 //Generates the number of the next unique proxy class name 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 * Verify the Class object with the same name as the interface resolved by the Class loader: in fact, verify whether the proxy interface is loaded by the Class loader 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 * Verify that the Class object is indeed an interface, which is why the jdk dynamic agent needs to implement the interface!!! 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 * //Verify that this interface is not duplicate 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 * Define a package name. If the interface of the proxy Class is non-public, all proxy classes will be generated into this package: anyway, this is to generate a package that stores the proxy Class class file containing non-public methods 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 //If the class you want to proxy does not contain non-public methods, use com.sun.proxy Package name as proxy class 82 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; 83 } 84 85 /* 86 * Choose a name for the proxy class to generate. 87 * Generate a name for Proxy Class: package name + $Proxy + number (automatic growth) 88 */ 89 long num = nextUniqueNumber.getAndIncrement(); 90 String proxyName = proxyPkg + proxyClassNamePrefix + num; 91 92 /* 93 * Generate the specified proxy class. 94 * Generate the specified proxy class: an array of its own 95 */ 96 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( 97 proxyName, interfaces, accessFlags); 98 try { 99 //Define a by the generated byte array Class Object: at last com.sun.proxy A package named $Proxy+num of Class Documents, such as: 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 }
After reading the source code, we know that the commonly referred to $Proxy0 comes from this way. This Class is actually a factory. It will define and generate a Class object of a Proxy Class. The generation process will determine the package name according to whether the method in the Proxy interface is public, and will also name the Proxy Class in the form of $Proxy + number. Let's see how the Proxy Class is generated according to line 96?
1 /** 2 * Generate a proxy class given a name and a list of proxy interfaces. 3 * Generate a proxy class based on the given name and a set of proxy interfaces 4 * 5 * @param name the class name of the proxy class The name of the proxy class 6 * @param interfaces proxy interfaces Proxy interface 7 * @param accessFlags access flags of the proxy class Access identity of proxy class 8 */ 9 public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) { 10 //1,Create a proxy generator object instance gen 11 ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); 12 /*2,Call the generateClassFile method of instance gen to generate a Class bytecode array. The following events are completed in this method: 13 Step 1: add hashCode, equals, toString and other methods 14 Step 2: add the methods in the proxy interface. According to the order of interfaces, the first added interface takes precedence 15 Step 3: verify the return type of the proxy method 16 Step 4: transfer the structures of FiledInfo and MethodInfo for all properties and methods in this class 17 Step 5: start writing DataOutputStream, that is, build a class object 18 Step 6: return byte array 19 */ 20 final byte[] classFile = gen.generateClassFile(); 21 //3,If the file needs to be saved, it will be generated in the specified path.class File 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,Return byte array 46 return classFile; 47 }
I have added comments to the above code. In fact, the core code is not here, but line 20, which returns a final modified byte array. The main steps in it have been added to the comments. In order to find out, I decided to attach its code to ensure that you will call cow force when you see it!!!
1 /** 2 * Generate a class file for the proxy class. This method drives the 3 * class file generation process. 4 * Generate a class file for the proxy object. This method will drive the class file generator 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 //Step 1: add hashCode,equals,toString Other methods 21 //Step 2: add the methods in the proxy interface. According to the order of interfaces, the first added interface takes precedence 22 //Step 3: verify the return type of the proxy method 23 //Step 4: transfer all attributes and methods in this class FiledInfo and MethodInfo Structure of 24 //Step 5: start writing DataOutputStream,That is, build a class object 25 //Step 6: return byte array 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 //Step 2: add the methods in the proxy interface. According to the order of interfaces, the first added interface takes precedence 36 // In fact, all the methods in the proxy interface are encapsulated into one ProxyMethod Object, which defines the method name, parameter type, return type, exception type, method source, local variable name in the method, etc 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 //Step 3: verify the return type in each method 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 //Step 4: transfer all attributes and methods in this class FiledInfo and MethodInfo Structure of 57 //The generated by each proxy method will be parsed here ProxyMethods Object, and finally generate two collections to store methods and parameters respectively 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 //Step 5: make sure you start final Type Class Before writing in the file, className,Parent class className And interfaces have sufficient constant pools 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 //Step 5: start writing DataOutputStream,That is, build a class object 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 * Core: is 0xCAFEBABE very familiar? Yes, this is the dynamic generation of a class object 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 //Step 6: return byte array 163 return bout.toByteArray(); 164 }
The addProxyMethod method method in lines 26, 27, 28 and 39 above encapsulates a method into a ProxyMethod object, which reflects the purpose that everything is an object in Java. These are not enough. Look at lines 65 to 74 again, encapsulate the method definition and method parameter definition in ProxyMethod into MthodInfo and FidleInfo objects. You can understand what it is here "Everything is an object" Let's look at 119 ~ 157 lines of code and see if it's shocking here. Yes, so am I! What's this? It's dynamically generating bytecode! The truth will be revealed when you see the magic number 0xCAFEBABE. The soul of jdk dynamic agent is to dynamically generate a class file, which contains calls to methods in the proxy object, which is the essence of java!!!
At this point, the source code of jdk dynamic agent is finished. Here is a summary:
1. jdk dynamic proxy is an interface based proxy. The proxy must be an interface or implement an interface, because the bottom layer will judge whether the target object is an interface type
2. Obtain the class object of the target interface by cloning
3. The Class object of the proxy object is generated by dynamically generating a bytecode file
4. Get the constructor of the proxy object through reflection, and generate an instance of the proxy object through the constructor
2, CGLIB dynamic proxy
CGLIB acts as a proxy for a class. First, let's see how it generates a proxy class. The following code creates a proxy class:
1 public T getProxy(Class<T> targetClass) { 2 Enhancer enhancer = new Enhancer(); 3 return (T) enhancer.create(targetClass, this); 4 }
Follow up step by step as usual, first with the new Enhancer() method:
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 * This method is used to create an Enhancer. It must be ensured that a new Enhancer will be created every time an object is generated, and it cannot be shared by multiple threads 7 * If you want to create an additional build instance, you need to use the Factory interface 8 * @see Factory 9 */ 10 public Enhancer() { 11 super(SOURCE); 12 }
This constructor calls the constructor of the parent class AbstractClassGenerator and creates a Source object. AbstractClassGenerator, as the name implies, is an abstract class generator. Let's look at the next step, 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 * If necessary, generate a new class and create a new instance using the specified callback function (if any), using the parameterless constructor of the parent class 6 * @return a new instance 7 */ 8 public Object create() { 9 classOnly = false; 10 argumentTypes = null; 11 return createHelper(); 12 }
Let's focus on the createHelper method in line 11:
1 private Object createHelper() { 2 //Pre check: if no filter is specified and there are multiple callback objects, throw an exception 3 preValidate(); 4 //Generate a key object 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 //Call the creation of the parent class create Method, according to key Create proxy class 14 Object result = super.create(key); 15 return result; 16 }
Through debug ging, it is found that the above test code will generate the following string of key s:
The most important is the type of the proxy class and the current proxy class, that is, the MethodInterceptor interface
Continue to track super.create(key); the specific implementation of this line of code:
1 //Define a volatile Modified cache variable CACHE 2 private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<ClassLoader, ClassLoaderData>(); 3 4 protected Object create(Object key) { 5 try { 6 //Get class loader 7 ClassLoader loader = getClassLoader(); 8 Map<ClassLoader, ClassLoaderData> cache = CACHE; 9 //Gets the data of the class loader from the cache 10 ClassLoaderData data = cache.get(loader); 11 //If the class loader does not exist, a new one is created 12 if (data == null) { 13 //See? Singleton mode based on double check mechanism!!!! 14 synchronized (AbstractClassGenerator.class) { 15 //Note: strictly speaking, this place has been carried out again set and get Operation, guarantee CACHE Not modified by other threads, which is probably the best Double Check Let's go!!! 16 cache = CACHE; 17 data = cache.get(loader); 18 //Perform a second inspection 19 if (data == null) { 20 //A new class loader is created only when the second check does not exist 21 Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache); 22 //In this method, the class loader points to a weak reference WeakReference 23 data = new ClassLoaderData(loader); 24 newCache.put(loader, data); 25 CACHE = newCache; 26 } 27 } 28 } 29 this.key = key; 30 //Judge whether to enable cache configuration, i.e cglib.useCache=true 31 //If the cache is enabled, it will be obtained from the cache. If it is not enabled, it will be generated and returned directly 32 Object obj = data.get(this, getUseCache()); 33 //If you return a Class,Then call firstInstance 34 if (obj instanceof Class) { 35 //Create the first instance: if a Class 36 return firstInstance((Class) obj); 37 } 38 //Otherwise, call nextInstance 39 return nextInstance(obj); 40 } catch (RuntimeException | Error ex) { 41 throw ex; 42 } catch (Exception ex) { 43 throw new CodeGenerationException(ex); 44 } 45 }
Here, a classic singleton mode based on double check is used to obtain class loader data in lines 12 to 28! Calm down and continue to look at the methods behind the two return s. The first is the first execution and the second is not the first execution:
1 protected Object firstInstance(Class type) throws Exception { 2 //If you just create a class, return directly 3 if (classOnly) { 4 return type; 5 } 6 else { 7 //Otherwise, it is created by reflection 8 return createUsingReflection(type); 9 } 10 } 11 12 //Create when the instance already exists 13 protected Object nextInstance(Object instance) { 14 EnhancerFactoryData data = (EnhancerFactoryData) instance; 15 16 //Similarly, if you just return Class Object, the of the instance is called generatedClass return 17 if (classOnly) { 18 return data.generatedClass; 19 } 20 //Otherwise, create a new instance 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 }
In fact, debug will find that it will be executed in the nextInstance method every time, because we want to obtain an instance of a proxy object. The first instance is implemented through reflection. Then, how to implement it in the nextInstance method?
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 }
See, the bottom layer still obtains the constructor through reflection, and then creates the object through the constructor's newInstance.
So far, the source code analysis of the two dynamic agents is almost the same. From JDK source code to Spring source code, you will always see a lot of interesting writing methods and best practices. This is probably where you don't find it boring to look at the source code. Ha ha ~ ~ let's summarize the content of this chapter:
1. JDK dynamic proxy is an interface based proxy, which is a feature of java language. The proxy class needs to implement the InvocationHandler interface, and the proxy is an interface
2. CGLIB dynamic proxy is a class based proxy, which is an integrated implementation of Spring and a third party. The proxy class needs to implement the MethodInterceptor interface, and the proxy is a class
3. For the generation of proxy objects, the reflection method is used at the bottom. First obtain the constructor and create an instance through the constructor
The above is the whole content of this chapter~~