This is the 9th day of my participation in Gwen Challenge
As mentioned earlier, enhancing the functionality of a class can extract the public interface and let the proxy class implement the interface to achieve the purpose of enhancement. Is there another way to do this? One of the three features of object-oriented —– inheritance, through inheritance to enhance the function of the parent class, which is the principle of CGLIB AOP implementation, Spring AOP has also adopted this scheme.
1. What is CGLIB
CGLIB (Code Generator Library) is a powerful and high-performance Code generation Library. It is widely used in AOP frameworks (Spring, Dynaop) to provide method interception operations. Hibernate, a popular ORM framework, also uses CGLIB to broker single-ended (many-to-one and one-to-one) associations.
2、为什么使用CGLIB
The CGLIB agent controls access to objects by introducing a level of indirection, primarily through bytecode manipulation. We know there is a dynamic proxy in Java that does the same thing, so why not just use Java dynamic proxies instead of CGLIB? The answer is that CGLIB is more powerful than JDK dynamic proxies, which, while simple to use, have a fatal flaw that they can only proxy interfaces. If the class being propped is a normal class with no interface, then Java dynamic proxies cannot be used.
3.CGLIB implements the principle of proxy
First create the target object:
public class UserManagerImpl { public void addUser(String userName) { System.out.println("UserManagerImpl add user name is:" + userName); } public void deleteUser(String userName) { System.out.println("UserManagerImpl delete user name is:" + userName); }}Copy the code
For this target class, if we wanted to implement AOP using dynamic proxies, we would have to write an enhanced interface and then have the target class implement the enhanced interface. Then we could use dynamic proxies to implement the target class’s enhancements. But if we didn’t want the target class to implement the other interfaces, We are left with CGLIB technology to implement the enhancement of the target class.
CGLIB dynamically creates a subclass of the target class and returns the object of that subclass, the enhanced object. The enhanced logic is done in the subclass. We know that subclasses either have the same functionality as or are more powerful than the parent class, so CGLIB is enhanced by creating a subclass object of the target class, so:
Target subclass = Target class + enhanced logic
4. Implement AOP with CGLIB
Importing Maven coordinates:
<dependency> <groupId>cglib</ artifactId> <version>3.2.0</version> </dependency>Copy the code
The CGLIB agent is implemented as follows:
We first implement a MethodInterceptor, and the method calls are forwarded to the Intercept () method of that class.
The proxy object is then retrieved through the CGLIB dynamic proxy when the UserManagerImpl (target object) is needed.
We still use the UserManagerImpl above as the target object, and then we implement a MethodInterceptor. Before implementing a MethodInterceptor, let’s look at what this interface is:
public interface MethodInterceptor extends Callback {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable;
}
Copy the code
Here’s what each parameter means:
- Object is a dynamically generated proxy class instance of CGLib
- Method is referenced by the proxied Method called by the entity class above
- Object[] is a list of parameter values
- MethodProxy is a proxy reference to a method for the generated proxy class.
Here is a MethodInterceptor implemented with the logic to create the enhanced object called myCglibCreator() method, which returns the enhanced object.
public class CglibFactory implements MethodInterceptor { public <T> T myCglibCreator(Class<T> clazz) { Enhancer enhancer = new Enhancer(); SetSuperclass (clazz); // Set the target class to the parent class. Cglib can not enhance the parent class of the final target class. // Set the callback interface. Here, the MethodInterceptor implements the class callback interface The intercept() method is hancer.setcallback (this); the intercept() method is hancer.setcallback (this); Return (T) enhancer.create(); } // The method of the callback interface is executed under the condition that: Override public Object Intercept (Object obj, Method Method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before action"); Object result = methodProxy.invokeSuper(obj, args); System.out.println("after action"); Return optional.ofnullable (result).map(r -> string.valueof (r).toupperCase ()).orelse (""); }}Copy the code
Object result = methodProxy. InvokeSuper (O, objects); Call the parent method of the methodProxy method on the proxy class instance (that is, the corresponding method in the entity class SomeService), return the return value of the target method result, and then implement the enhanced logic to uppercase the returned string. Here is the test code:
@Test public void test_aop() { UserManagerImpl proxy = new CglibFactory().myCglibCreator(UserManagerImpl.class); String result = proxy.addUser("steven"); System.out.println("result is:" + result); } Before Action UserManagerImpl add user name is: Steven after action result is: StevenCopy the code
In the above code, we use CGLIB’s Enhancer to specify the target object, the object that actually handles the proxy logic, and finally get the proxy object by calling the create() method. Calls to the object all the final method will be forwarded to MethodInterceptor. Intercept () method, the intercept () method, we can join any logic, such as modifying method parameters, to join the log function, security function, etc.; By calling the methodProxy.invokesuper () method, we forward the call to the original object, in this case, the SomeService concrete method. The CGLIG MethodInterceptor acts much like the InvocationHandler in the JDK’s dynamic proxy agent, as a way station for method calls.
5, summary
CGLIB is a convenient way to implement dynamic enhancement, but CGLIB also has its drawbacks. That is, the target class must be inheritable. If the target class is not inheritable, then we cannot use CGLIB to enhance the class. CGLIB’s implementation of AOP is called subclass-oriented dynamic enhancement.
Main differences:
- Java dynamic proxies can only be used to Proxy interfaces, not common classes (because the parent class of all generated Proxy classes is Proxy, Java class inheritance mechanism does not allow multiple inheritance; CGLIB can delegate common classes;
- Java dynamic proxy uses Java’s native reflection API to operate and is efficient in generating classes. CGLIB uses the ASM framework to operate directly on bytecode, which is efficient in class execution