Easy Tutorial
❮ Android Tutorial Viewflipper Htmlcss Make A Website ❯

CGLIB (Code Generation Library) Introduction and Principles

Category Programming Techniques

I. What is CGLIB?

CGLIB is a powerful, high-performance code generation package. It provides proxies for classes that do not implement interfaces, complementing JDK's dynamic proxy capabilities. Typically, Java's dynamic proxy can be used 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


II. CGLIB Principles

CGLIB Principles: Dynamically generate a subclass of the class to be proxied, and override all non-final methods of the class to be proxied in the subclass. The subclass uses method interception techniques to intercept all calls to the parent class methods, seamlessly weaving in cross-cutting logic. It is faster than JDK dynamic proxy using Java reflection.

CGLIB Underlying: Uses the bytecode processing framework ASM to transform bytecode and generate new classes. Direct use of ASM is discouraged as it requires a deep understanding of the JVM's internal structure, including the format of class files and instruction sets.

CGLIB Drawbacks: Cannot proxy final methods.


III. 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.


IV. Why Use CGLIB?

CGLIB proxy mainly introduces an indirect level to objects by manipulating bytecode to control object access. We know that Java has a dynamic proxy that does the same thing, so why don't we just use Java's dynamic proxy instead of 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 a regular class without an interface, then Java dynamic proxy cannot be used.


V. CGLIB Composition Structure

CGLIB uses ASM (a compact and powerful bytecode manipulation framework) at its core to manipulate 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. Direct use of ASM is discouraged as it requires sufficient understanding of the Java bytecode format.


VI. CGLIB API

1. Jar Packages:

2. CGLIB Class Library:

Due to the scarcity of basic code, learning can be difficult, 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.

This article introduces the implementation of a dynamic proxy through MethodInterceptor and Enhancer.

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

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, which means that only the 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.

II. Implementation with CGLib:

Implementing dynamic proxy with CGLib is not limited by the requirement that the proxy class must implement an interface, and CGLib uses the ASM bytecode generation framework at the bottom layer, using bytecode technology to generate proxy classes, 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 will be provided to introduce the implementation of dynamic proxy

System.out.println("filter method3 ==2");
return 2;
}
return 0;
}

}

Where 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: Method interceptor
         (2) NoOp.INSTANCE: This NoOp signifies no operation, meaning that the proxy class directly calls the proxied method without interception.
         (3) FixedValue: Indicates locking the method return value, regardless of what value the proxied class's method returns, the callback method returns a fixed value.
         */
        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 value the proxied class's method returns, the callback method returns a fixed value.
 * @author zghw
 *
 */
public class TargetResultFixed implements FixedValue {

    /**
     * This class implements the FixedValue interface, and locks the callback value to 999
     * (integer type, the method using the FixedValue type callback in CallbackFilter is defined as getConcreteMethodFixedValue, and the method's return value is of integer type).
     */
    @Override
    public Object loadObject() throws Exception {
        System.out.println("Locking result");
        Object obj = 999;
        return obj;
    }

}

5. Lazy Loading Object

I. Function:

Speaking of lazy loading, it should be frequently encountered, especially when using Hibernate. This article will analyze the implementation of lazy loading through an example. The LazyLoader interface inherits from 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.

However, the difference between Dispatcher and LazyLoader is that LazyLoader only triggers the proxy class callback method when the lazy loading attribute is accessed for the first time, while Dispatcher triggers the proxy class callback method every time the lazy loading attribute is accessed.

II. Example:

First, define an entity class LoaderBean, which 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 for the first time
     * @return
     */
    private PropertyBean createPropertyBean() {
        /**
         * Use cglib for lazy loading to add proxy to the object that needs to be lazily loaded. When obtaining the property of this object, first initialize the object through the proxy class callback method.
         * When you don't need to load this object, as long as you don't access the properties inside this object, the object will not be initialized (in the implementation of CGLib, as long as you access the getter method of the property inside this object,
         * it will automatically trigger the proxy class callback).
         */
        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()
Public class ConcreteClassLazyLoader implements LazyLoader {
    /**
     * Adds a proxy to the object that needs lazy loading. When the property of this object is accessed, the object is first initialized through the proxy class callback method.
     * When the object does not need to be loaded, as long as the properties inside the object are not accessed, the object will not be initialized (in the CGLib implementation, as long as the getter method of the property inside the object is accessed,
     * it will automatically trigger the proxy class callback).
     */
    @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. Interface Generator InterfaceMaker

I. Function:

InterfaceMaker dynamically generates an interface that includes all methods defined in a 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 methods from 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 interception for interface method
        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 article link:

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

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

**Click to Share Notes

Cancel

-

-

-

English:

❮ Android Tutorial Viewflipper Htmlcss Make A Website ❯