Close reading of Spring source code -- in-depth analysis of the underlying principle of dynamic proxy

Keywords: Java Spring

Previous review:

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~~

Posted by alireza on Wed, 01 Dec 2021 00:38:26 -0800