1. What is CGLIB?

CGLIB is a powerful, high-performance code generation package. It provides proxies for classes that do not implement interfaces, and provides a good supplement to JDK's dynamic proxy. Usually you can use Java's dynamic proxy to create proxies, but when the class to be proxied does not implement an interface or for better performance, CGLIB is a good choice.

As an open-source project, CGLIB's code is hosted on GitHub at:https://github.com/cglib/cglib


2. CGLIB Principle

CGLIB principle: Dynamically generates a subclass of the class to be proxied, and the subclass overrides all non-final methods of the class to be proxied. In the subclass, method interception technology is used to intercept all calls to parent class methods, and cross-cutting logic is woven in accordingly. It is faster than JDK dynamic proxy using Java reflection.

CGLIB underlying layer: It uses the bytecode processing framework ASM to transform bytecode and generate new classes. Direct use of ASM is discouraged because it requires you to be very familiar with the JVM internal structure, including the format of class files and the instruction set.

CGLIB disadvantages: Final methods cannot be proxied.


3. CGLIB Applications

It is widely used by many AOP frameworks, such as Spring AOP and dynaop. Hibernate uses CGLIB to proxy single-ended (many-to-one and one-to-one) associations.


4. Why Use CGLIB?

CGLIB proxy mainly operates on bytecode to introduce an indirection level for objects, in order to control access to objects. We know that Java has a dynamic proxy that also does this, so why not directly use Java dynamic proxy and instead use CGLIB? The answer is that CGLIB is more powerful than JDK dynamic proxy. Although JDK dynamic proxy is simple and easy to use, it has a fatal flaw: it can only proxy interfaces. If the class to be proxied is an ordinary class without an interface, then Java dynamic proxy cannot be used.


5. CGLIB Composition Structure

CGLIB's underlying layer uses ASM (a compact and powerful bytecode manipulation framework) to operate on bytecode and generate new classes. In addition to the CGLIB library, scripting languages (such as Groovy and BeanShell) also use ASM to generate bytecode. ASM uses a SAX-like parser to achieve high performance. We do not encourage direct use of ASM because it requires sufficient understanding of the Java bytecode format.


6. CGLIB API

1. Jar package:

  • cglib-nodep-2.2.jar: When using the nodep package, there is no need to associate the ASM jar package; the jar package contains ASM classes internally.
  • cglib-2.2.jar: When using this jar package, you need to associate the ASM jar package; otherwise, a runtime error will occur.

2. CGLIB library:

Since the underlying code is minimal, it is somewhat difficult to learn, mainly due to the lack of documentation and examples, which is also a shortcoming of CGLIB.

The CGLIB version used in this series is 2.2.

  • net.sf.cglib.core: Low-level bytecode processing classes, most of which are related to ASM.
  • net.sf.cglib.transform: Transformation of classes and class files at compile time or runtime
  • net.sf.cglib.proxy: Classes that implement proxy creation and method interceptors
  • net.sf.cglib.reflect: Classes that implement fast reflection and C#-style proxies
  • net.sf.cglib.util: Utility classes such as collection sorting
  • net.sf.cglib.beans: Utility classes related to JavaBean

This article introduces implementing a dynamic proxy through MethodInterceptor and Enhancer.

1. First, let's talk about the dynamic proxy in JDK:

The dynamic proxy in JDK is implemented through the reflection class Proxy and the InvocationHandler callback interface. However, the class to be dynamically proxied in JDK must implement an interface. That is to say, only methods defined in the interface implemented by the class can be proxied. This has certain limitations in actual programming, and the efficiency of using reflection is not very high.

2. Using CGLib to implement:

Using CGLib to implement dynamic proxy is completely free from the restriction that the proxy class must implement an interface. Moreover, CGLib's underlying layer uses the ASM bytecode generation framework to generate proxy classes using bytecode technology, which is more efficient than using Java reflection. The only thing to note is that CGLib cannot proxy methods declared as final, because the principle of CGLib is to dynamically generate a subclass of the proxied class.

Below, an example is used to introduce the implementation of dynamic proxy using CGLib.

1. Proxied class:

First, define a class that does not implement any interface.

package com.zghw.cglib; /** * A target class that does not implement an interface and requires CGlib dynamic proxy. * * @author zghw * */ public class TargetObject { public String method1(String paramName) { return paramName; } public int method2(int count) { return count; } public int method3(int count) { return count; } @Override public String toString() { return "TargetObject []"+ getClass(); } }

2. Interceptor:

Define an interceptor. When the target method is called, CGLib will call back the MethodInterceptor interface method for interception to implement your own proxy logic, similar to the InvocationHandler interface in JDK.

package com.zghw.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * Target object interceptor, implements MethodInterceptor * @author zghw * */ public class TargetInterceptor implements MethodInterceptor{ /** * RewriteMethods拦截InMethodsbeforeandMethodsPost-increment入业affair * Object objisTarget Object * Method methodisTargetMethods * Object[] params isparameters, * MethodProxy proxy CGlibMethodssurrogate pair象*/ @Override public Object intercept(Object obj, Method method, Object[] params, MethodProxy proxy) throws Throwable { System.out.println("Before call"); Object result = proxy.invokeSuper(obj, params); System.out.println("After call"+result); return result; } }

Parameters: Object is the proxy class instance dynamically generated by CGLib, Method is the reference to the proxied method called by the entity class above, Object[] is the list of parameter values, and MethodProxy is the proxy reference to the method generated by the proxy class.

Return: The value returned from the method call on the proxy instance.

Among them,proxy.invokeSuper(obj,arg)Invoke the superclass method of the proxy method on the proxy class instance (i.e., the corresponding method in the entity class TargetObject).

In this example, only one sentence is printed before and after calling the proxied class method. Of course, in actual programming, it can be other complex logic.

3. Generate the dynamic proxy class:

package com.zghw.cglib; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.CallbackFilter; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.NoOp; public class TestCglib { public static void main(String args[]) { Enhancer enhancer =new Enhancer(); enhancer.setSuperclass(TargetObject.class); enhancer.setCallback(new TargetInterceptor()); TargetObject targetObject2=(TargetObject)enhancer.create(); System.out.println(targetObject2); System.out.println(targetObject2.method1("mmm1")); System.out.println(targetObject2.method2(100)); System.out.println(targetObject2.method3(200)); } }

Here, the Enhancer class is a bytecode enhancer in CGLib. It can conveniently extend the class you want to process, and we will see it often later.

First set the proxied class TargetObject as the parent class, then set the interceptor TargetInterceptor, and finally execute enhancer.create() to dynamically generate a proxy class, and cast it from Object to the parent type TargetObject.

Finally, call the method on the proxy class.

4. Callback filter CallbackFilter

1. Function:

When CGLib performs callbacks, you can set different callback logic for different methods, or even not perform callbacks at all.

There is no similar feature in JDK dynamic proxy; the call to the InvocationHandler interface method is effective for all methods in the proxy class.

Define a class that implements the CallbackFilter interface:

package com.zghw.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.CallbackFilter; /** * Callback method filtering * @author zghw * */ public class TargetMethodCallbackFilter implements CallbackFilter { /** * FilterMethods * returnedValue isNumbers,generationTablealreadyCallbackin the arraythe index ofPosition,wanttouseofCallback*/ @Override public int accept(Method method) { if(method.getName().equals("method1")){ System.out.println("filter method1 ==0"); return 0; } if(method.getName().equals("method2")){ System.out.println("filter method2 ==1"); return 1; } if(method.getName().equals("method3")){ System.out.println("filter method3 ==2"); return 2; } return 0; } }

The return value is the position index of each method of the proxied class in the callback array Callback[] (see below).

package com.zghw.cglib; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.CallbackFilter; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.NoOp; public class TestCglib { public static void main(String args[]) { Enhancer enhancer =new Enhancer(); enhancer.setSuperclass(TargetObject.class); CallbackFilter callbackFilter = new TargetMethodCallbackFilter(); /** * (1)callback1:Methods拦截device (2)NoOp.INSTANCE:ThisNoOpRepresentsno operator, i.e.什么OperationalsoNo做,Proxy classstraightJieCallBybrokermethod ofNoenterline拦截。 (3)FixedValue:RepresentsLockdecidemethod returnsvalue,no matterByProxy classmethod ofreturn什么value,回调MethodsallreturnFixedvalue.*/ Callback noopCb=NoOp.INSTANCE; Callback callback1=new TargetInterceptor(); Callback fixedValue=new TargetResultFixed(); Callback[] cbarray=new Callback[]{callback1,noopCb,fixedValue}; //enhancer.setCallback(new TargetInterceptor()); enhancer.setCallbacks(cbarray); enhancer.setCallbackFilter(callbackFilter); TargetObject targetObject2=(TargetObject)enhancer.create(); System.out.println(targetObject2); System.out.println(targetObject2.method1("mmm1")); System.out.println(targetObject2.method2(100)); System.out.println(targetObject2.method3(100)); System.out.println(targetObject2.method3(200)); } }
package com.zghw.cglib; import net.sf.cglib.proxy.FixedValue; /** Indicates locking the method return value: regardless of what the proxied class's method returns, the callback method always returns a fixed value. * @author zghw * */ public class TargetResultFixed implements FixedValue{ /** * thatClassImplementationFixedValueinterface,at the same timeLockdecide回调Value is999 * (integer,CallbackFilterMediumdefinedUsageFixedValuetype回调method ofisgetConcreteMethodFixedValue, thismethod returnsValue isinteger)。*/ @Override public Object loadObject() throws Exception { System.out.println("Locked result"); Object obj = 999; return obj; } }

5. Lazy loading objects

1. Function:

Speaking of lazy loading, you should often encounter it, especially when using Hibernate. This article will analyze the implementation of lazy loading through an example. The LazyLoader interface inherits Callback, so it is also a type of Callback in CGLib.

Another lazy loading interface is Dispatcher.

The Dispatcher interface also inherits from Callback and is also a type of callback.

But the difference between Dispatcher and LazyLoader is: LazyLoader only triggers the proxy class callback method when the lazy-loaded property is accessed for the first time, while Dispatcher triggers the proxy class callback method every time the lazy-loaded property is accessed.

II. Example:

First, define an entity class LoaderBean. This Bean has a property PropertyBean that needs lazy loading.

package com.zghw.cglib; import net.sf.cglib.proxy.Enhancer; public class LazyBean { private String name; private int age; private PropertyBean propertyBean; private PropertyBean propertyBeanDispatcher; public LazyBean(String name, int age) { System.out.println("lazy bean init"); this.name = name; this.age = age; this.propertyBean = createPropertyBean(); this.propertyBeanDispatcher = createPropertyBeanDispatcher(); } /** * Only lazy load on the first time * @return */ private PropertyBean createPropertyBean() { /** * Use cglib for lazy loading. Add a proxy to the object that needs lazy loading. When obtaining the object's properties, initialize the object through the proxy class callback method first. * When the object does not need to be loaded, as long as the properties within the object are not accessed, the object will not be initialized (in CGLib's implementation, as long as the getter methods of the object's properties are accessed, * the proxy class callback will be triggered automatically).*/ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PropertyBean.class); PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class, new ConcreteClassLazyLoader()); return pb; } /** * Lazy load every time * @return */ private PropertyBean createPropertyBeanDispatcher() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PropertyBean.class); PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class, new ConcreteClassDispatcher()); return pb; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public PropertyBean getPropertyBean() { return propertyBean; } public void setPropertyBean(PropertyBean propertyBean) { this.propertyBean = propertyBean; } public PropertyBean getPropertyBeanDispatcher() { return propertyBeanDispatcher; } public void setPropertyBeanDispatcher(PropertyBean propertyBeanDispatcher) { this.propertyBeanDispatcher = propertyBeanDispatcher; } @Override public String toString() { return "LazyBean [name=" + name + ", age=" + age + ", propertyBean=" + propertyBean + "]"; } }
package com.zghw.cglib; public class PropertyBean { private String key; private Object value; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } @Override public String toString() { return "PropertyBean [key=" + key + ", value=" + value + "]" +getClass(); } }
package com.zghw.cglib; import net.sf.cglib.proxy.LazyLoader; public class ConcreteClassLazyLoader implements LazyLoader { /** * Add a proxy to the object that needs lazy loading. When obtaining the object's properties, initialize the object through the proxy class callback method first. * When the object does not need to be loaded, as long as the properties within the object are not accessed, the object will not be initialized (in CGLib's implementation, as long as the getter methods of the object's properties are accessed, * the proxy class callback will be triggered automatically).*/ @Override public Object loadObject() throws Exception { System.out.println("before lazyLoader..."); PropertyBean propertyBean = new PropertyBean(); propertyBean.setKey("zghw"); propertyBean.setValue(new TargetObject()); System.out.println("after lazyLoader..."); return propertyBean; } }
package com.zghw.cglib; import net.sf.cglib.proxy.Dispatcher; public class ConcreteClassDispatcher implements Dispatcher{ @Override public Object loadObject() throws Exception { System.out.println("before Dispatcher..."); PropertyBean propertyBean = new PropertyBean(); propertyBean.setKey("xxx"); propertyBean.setValue(new TargetObject()); System.out.println("after Dispatcher..."); return propertyBean; } }

6. InterfaceMaker - Interface Generator

I. Function:

InterfaceMaker dynamically generates an interface that contains all methods defined by the specified class.

II. Example:

package com.zghw.cglib; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.InterfaceMaker; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class TestInterfaceMaker { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { InterfaceMaker interfaceMaker =new InterfaceMaker(); //Extract the methods of a class to generate interface methods. interfaceMaker.add(TargetObject.class); Class<?> targetInterface=interfaceMaker.create(); for(Method method : targetInterface.getMethods()){ System.out.println(method.getName()); } //Interface proxy and set proxy interface method interception. Object object = Enhancer.create(Object.class, new Class[]{targetInterface}, new MethodInterceptor(){ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if(method.getName().equals("method1")){ System.out.println("filter method1 "); return "mmmmmmmmm"; } if(method.getName().equals("method2")){ System.out.println("filter method2 "); return 1111111; } if(method.getName().equals("method3")){ System.out.println("filter method3 "); return 3333; } return "default"; }}); Method targetMethod1=object.getClass().getMethod("method3",new Class[]{int.class}); int i=(int)targetMethod1.invoke(object, new Object[]{33}); Method targetMethod=object.getClass().getMethod("method1",new Class[]{String.class}); System.out.println(targetMethod.invoke(object, new Object[]{"sdfs"})); } }

Original address:

https://blog.csdn.net/zghwaicsdn/article/details/50957474

https://blog.csdn.net/danchu/article/details/70238002