Advanced Spring AOP-source code implementation (1) Dynamic proxy technology, springaop
Before formally entering the source code implementation of Spring AOP, we need to prepare a certain foundation, that is, the core of Aspect-Oriented Programming-dynamic proxy. Dynamic proxy is actually a structural design model. JDK has already prepared this design mode for us. However, this kind of dynamic proxy provided by JDK has two disadvantages:
In view of the above two shortcomings, the Second Dynamic proxy technology-CGLIB (Code Generation Library) emerged ). This proxy technology requires no target object implementation interface (which greatly extends the scope of use), and is based on bytecode (which is more efficient than reflection ). Of course, it is not completely defect-free,Because it cannot proxy the final Method(Because its dynamic proxy is actually a subclass of the generated target object ).
When generating proxy objects in Spring AOP, you can use both the dynamic proxy technology of JDK and the dynamic proxy technology of CGLIB. This chapter first gives a brief introduction to the dynamic proxy technology of both, easy to understand the source code.
JDK dynamic proxy technology
JDK dynamic proxy technology requires that the target object must implement an interface:
1 package proxy;2 3 /**4 * Created by Kevin on 2017/11/8.5 */6 public interface Subject {7 void sayHello();8 }
Next we need to proxy the real object, that is, the target object:
1 package proxy; 2 3/** 4 * target object, that is, the object to be proxy 5 * Created by Kevin on. 6 */7 public class RealSubject implements Subject {8 public void sayHello () {9 System. out. println ("hello world"); 10} 11}
This is a real object. We hope to enhance the sayHello method of this class without modifying the original code logic. Using the dynamic JDK proxy technology, we need to implement the invoke method in the InvocationHandler interface:
1 package proxy; 2 3 import java. lang. reflect. invocationHandler; 4 import java. lang. reflect. method; 5 6 public class ProxySubject implements InvocationHandler {7 private Object target; 8 9 public ProxySubject (Object target) {10 this.tar get = target; 11} 12 13 public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {14 System. out. println ("before call"); 15 Object object = method. invoke (target, args); 16 System. out. println ("after call"); 17 return object; 18} 19}
Row 3: In the invoke method, we can see that we have added methods Before and After calling the method of the target object. This is actually the meaning of Before and After notifications in AOP.
Add the test code:
1 package proxy; 2 3 import java. lang. reflect. proxy; 4 5/** 6 * Created by Kevin on. 7 */8 public class Test {9 public static void main (String [] args) {10 Subject subject = (Subject) Proxy. newProxyInstance (RealSubject. class. getClassLoader (), RealSubject. class. getInterfaces (), new ProxySubject (new RealSubject (); 11 subject. sayHello (); 12 13 // view the subject object type 14 System. out. println (subject. getClass (). getName (); 15} 16}
Execution result:
As we can see, it is almost the same as AOP. As mentioned above, dynamic proxy is the core of AOP. At the same time, we can see that the type of the proxy class is: com. sun. proxy. $ Proxy0. When we go deep into the JDK source code, we will see why.
Back to the example above, we use Proxy. newProxyInstance generates a proxy class. Obviously, this class is generated at Run-Time (runtime). That is to say, the generation of proxy classes in JDK dynamic proxy comes from the support of the Java reflection mechanism.
In the preceding example, the InvocationHandler class is named "ProxySubject", which is actually inaccurate. We can see that the type of the proxy class is not ProxySubject, this class is actually the method for processing enhancement, that is, the implementation logic in invoke. At last, it is not the agent class of this type, nor is it the generated agent class, therefore, the name is inaccurate.
First, we start from Proxy. newProxyInstance to study how JDK generates Proxy classes.
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
This method has three parameters. To understand JVM class loading, you may know that two conditions are required for determining the same class:
To generate a proxy for the target object, you must first ensure that its class loaders are the same. Therefore, you must pass the class loaders of the target object as parameters; secondly, JDK dynamic proxy technology requires that both the proxy class and the target object inherit from the same interface, so the interface of the target object must be passed as a parameter. Finally, the InvocationHandler must be passed, which is the main character, because the enhancement logic of the target object is passed in this implementation class, so that the proxy class can call it.
There are three steps to create a Proxy class in the Proxy. newProxyInstance method:
1. Check
1 public static Object newProxyInstance (ClassLoader loader, 2 Class <?> [] Interfaces, 3 InvocationHandler h) 4 throws IllegalArgumentException 5 {6 Objects. requireNonNull (h); // 1.1 check whether the parameter is null 7 8 final Class <?> [] Intfs = interfaces. clone (); 9 final SecurityManager sm = System. getSecurityManager (); // obtain the security manager. The security manager is used to control access to external resources. 10 if (sm! = Null) {11 checkProxyAccess (Reflection. getCallerClass (), loader, intfs); // 1.2 check whether there is access permission 12}
In the source code above, there is a process of obtaining the security manager and checking whether it has access permissions. Security Manager may not be very commonly used in practice. It is used to control permissions for programs to access certain sensitive resources, that is, to protect programs. You do not need to explore it carefully here. You just need to know about it. The permission check here is actually a ClassLoader check. For example, some programs do not allow you to proxy the class. In this case, you can add the security manager to prevent you from proxy for this class.
2. Get the proxy type
Class <?> Cl = getProxyClass0 (loader, intfs); // obtain the proxy class type
The class loader of the target object and its inherited interface can be used to obtain the type of the proxy class.
1/** 2 * Generate a proxy class. must call the checkProxyAccess method 3 * to perform permission checks before calling this. 4 * as you can see from the comments, this method is used to generate a proxy class. Before calling this method, make sure that 5 * has performed permission check. 6 */7 private static Class <?> GetProxyClass0 (ClassLoader loader, 8 Class <?>... Interfaces) {9 if (interfaces. length> 65535) {// a class can implement up to 65535 interfaces 10 throw new IllegalArgumentException ("interface limit exceeded"); 11} 12 return proxyClassCache. get (loader, interfaces); // obtain the proxy class from the cache first. If it does not exist, it is created through ProxyClassFactory, which involves a complicated proxy cache mechanism, this article focuses on the source code implementation of the dynamic proxy process, and further studies the cache mechanism of the dynamic proxy. 13}
The above method returns the com. sun. proxy. $ Proxy0 proxy type. The following will generate the proxy class through this proxy type.
3. Generate proxy class
1 try {2 if (sm! = Null) {3 checkNewProxyPermission (Reflection. getCallerClass (), cl); // check the generated proxy type for permission check. Of course, the premise is to pass the System. setSecurityManager: Set security management class 4} 5 6 final Constructor <?> Cons = cl. getConstructor (constructorParams); // get the constructor through reflection. cl is the proxy type. Its constructor parameter type is InvocationHandler. Therefore, the parameter is passed into InvocationHandler 7 final InvocationHandler ih = h; 8 if (! Modifier. isPublic (cl. getModifiers () {// determines whether the constructor modifier of the target object is public. If not, the proxy class cannot be generated and null 9 AccessController is returned. doPrivileged (new PrivilegedAction <Void> () {10 public Void run () {11 cons. setAccessible (true); 12 return null; 13} 14}); 15} 16 return cons. newInstance (new Object [] {h}); // finally generate proxy class 17} catch (IllegalAccessException | InstantiationException e) {18 throw new InternalError (e. toString (), e); 19} catch (InvocationTargetException e) {20 Throwable t = e. getCause (); 21 if (t instanceof RuntimeException) {22 throw (RuntimeException) t; 23} else {24 throw new InternalError (t. toString (), t); 25} 26} catch (NoSuchMethodException e) {27 throw new InternalError (e. toString (), e); 28}
The above is the process of generating proxy classes through JDK dynamic proxy, which involves the caching mechanism of dynamic proxy and the process of generating the bytecode of the proxy class. Due to the complexity, this article will not be introduced for the time being. It can be clearly seen that the underlying layer of JDK dynamic proxy is implemented through the Java reflection mechanism, and the target object must inherit from an interface to generate its proxy class.
Next we will discuss another dynamic proxy technology, CGLib.
CGLib dynamic proxy technology
To create a proxy through CGLib, You need to introduce the jar package. Its pom. xml dependency is as follows:
<Dependency> <groupId> cglib </groupId> <artifactId> cglib </artifactId> <version> 3.2.4 </version> </dependency>View Code
As mentioned above, the CGLib dynamic proxy technology does not require the target object to implement a self-defined interface:
1 package cglibproxy; 2 3/** 4 * target object (class to be proxy) 5 * Created by Kevin on. 6 */7 public class RealSubject {8 public void sayHello () {9 System. out. println ("hello"); 10} 11}
Next we will use the CGLib proxy class:
1 package cglibproxy; 2 3 import net. sf. cglib. proxy. enhancer; 4 import net. sf. cglib. proxy. methodInterceptor; 5 import net. sf. cglib. proxy. methodProxy; 6 7 import java. lang. reflect. method; 8 9/** 10 * proxy class 11 * Created by Kevin on. 12 */13 public class ProxySubject implements MethodInterceptor {14 private Enhancer enhancer = new Enhancer (); 15 16 public Object getProxy (Class clazz) {17 enhancer . SetSuperclass (clazz); 18 enhancer. setCallback (this); 19 return enhancer. create (); // used to create a target object proxy class without parameters. For a parameter constructor, Enhancer is called. create (Class [] argumentTypes, Object [] arguments). The first parameter indicates the parameter type, and the second parameter indicates the parameter value. 20} 21 22 @ Override23 public Object intercept (Object object, Method method, Object [] args, MethodProxy methodProxy) throws Throwable {24 System. out. println ("before call"); 25 Object result = methodProxy. invokeSuper (object, args); 26 System. out. println ("after call"); 27 return result; 28} 29}
You can see that you also need to implement an interface-MethodIntercept, and implement a method similar to invoke-intercept.
Add the test code:
1 package cglibproxy; 2 3 /** 4 * Created by Kevin on 2017/11/6. 5 */ 6 public class Main { 7 public static void main(String[] args) { 8 RealSubject subject = (RealSubject) new ProxySubject().getProxy(RealSubject.class); 9 subject.sayHello();10 System.out.println(subject.getClass().getName());11 }12 }
Execution result:
The execution result is the same as that of the JDK dynamic proxy. The difference is that the type of the proxy class is cglibproxy. RealSubject $ EnhancerByCGLIB $ cb568e93. Next, let's look at how CGLib generates proxy classes.
The getProxy method in the ProxySubject class is generated, and two parameters are passed in:
Enhancer. setSuperclass (clazz); // sets the class enhancer. setCallback (this) to be proxy; // sets the callback Method
After setting the parameters, call enhancer. create () to create a proxy class.
1 public Object create () {2 classOnly = false; // if this field is set to false, a specific Object proxy class is returned. In createClass () the method sets classOnly = true to return the class proxy class. 3 argumentTypes = null; // create a proxy class for the target object without parameters, so there is no parameter, so the parameter type is set to null4 return createHelper (); 5}
It seems that you are still calling a method called createHelper.
1 private Object createHelper () {2 preValidate (); // perform some validation in advance. 3 // 4 ...... 5}
1 private void preValidate () {2 if (callbackTypes = null) {3 callbackTypes = CallbackInfo. determineTypes (callbacks, false); 4 validateCallbackTypes = true; 5} // check whether the callback method is empty 6 if (filter = null) {// check whether a filter is set, if multiple callback methods are set, you need to set the filter 7 if (callbackTypes. length> 1) {8 throw new IllegalStateException ("Multiple callback types possible but no filter specified"); 9} 10 filter = ALL_ZERO; 11} 12}
Check the remaining code of createHelper:
1 private Object createHelper () {2 preValidate (); 3 Object key = KEY_FACTORY.newInstance (superclass! = Null )? Superclass. getName (): null, 4 ReflectUtils. getNames (interfaces), 5 filter = ALL_ZERO? Null: new WeakCacheKey <CallbackFilter> (filter), 6 callbackTypes, 7 useFactory, 8 interceptDuringConstruction, 9 serialVersionUID); 10 this. currentKey = key; // cache mechanism is also used in CGLib. This Code is also complicated. Do not analyze cache policies for the moment. 11 Object result = super. create (key); // use bytes to implement and create a proxy class Object 12 return result; 13}
The two dynamic proxy technologies JDK and CGLib are introduced without much in-depth research, especially the implementation of the caching mechanism between the two.
In addition, the performance of CGLib is higher than that of JDK at the beginning, which is not accurate. Perhaps this is true under special conditions, because I found that the dynamic proxy efficiency of JDK 8 is very high, or even slightly higher than CGLib, but the efficiency in the JDK6 environment is relatively low. Therefore, the performance of CGLib is usually higher than that of JDK dynamic proxy, which is a traditional concern. In fact, Java has been constantly optimizing the dynamic proxy performance, in a later version of JDK, you can use the dynamic proxy of JDK native.
This is a public number that can add buff to programmers.