“Feed, hello, I am XX baba company of technical interview officer, excuse me you are zhang xiaoshuai”. The voice came from the other end of the line

“Yes, hello.” Small handsome secretly pleased, big factory finally find me.

“Now let’s have a telephone interview. Please introduce yourself first.”

“Balabalabla…” Xiao Shuai outlined his previous experience

What is the difference between Cglib and JDK dynamic proxy?

“Well, it’s even)…” , Zhang Xiaoshuai meng, the scene was awkward.

.

The interview happened just now, because the first question was planted, the back of the interviewer’s question, the little handsome basic what confidence did not, at this time the little handsome mood for a long time can not calm, “this is the big factory interview?” , small handsome mutter, he never thought of is the first question planted.

Does the person in front of the screen know? Next, follow the old cat to have a good brush up.


Talk about dynamic proxy, let’s start from the proxy model.

The proxy pattern

The proxy pattern is defined as providing a proxy object to an object that controls references to the original object.

The main solution is to solve the problem of accessing objects directly, for example, the object to be accessed on a remote machine. In object-oriented systems, some object for some reason (such as the object creation overhead is very large, or some operations require safety control, or need to access) outside the process, will have direct access to caused a lot of trouble for the user or the system structure, during a visit to this object we can add an object to this access layer.

For example, if train tickets are a target and we want to buy them, we don’t have to go to the train station to buy them. In fact, we can also buy them from many agencies. For example, pig eight quit to find Gao Cuilan results is sun Wukong change, can be understood as follows: the appearance of Gao Cuilan abstract out, GAO Cuilan himself and Sun Wukong have realized the interface, pig eight quit visit Gao Cuilan can not see this is Sun Wukong, so the Monkey King is Gao Cuilan agent class.

Static proxy mode

Code demonstration: Let’s buy train tickets through the case to demonstrate the agent mode, the specific code is as follows:

/ * * *@Author: old cats and dogs@Description: * /
public interface Ticket {
    void getTicket(a);
}
Copy the code

Railway station abstract interface with ticket function

/ * * *@Author: old cats and dogs@Description: Train station */
public class RailwayStation implements Ticket {

    @Override
    public void getTicket(a) {
        System.out.println("Bought a train ticket."); }}Copy the code

Train station agent class abstract interface with ticket function

/ * * *@Author: old cats and dogs@Description: Proxy class *@Date: 2021/12/22 5:35 下午
 */
public class RailwayAgencyProxy implements Ticket{
    private RailwayStation railwayStation;

    public RailwayAgencyProxy(RailwayStation railwayStation) {
        this.railwayStation = railwayStation;
    }

    @Override
    public void getTicket(a) { railwayStation.getTicket(); }}Copy the code

This is the static proxy pattern.

Advantages:

  1. The function of the target object can be extended in accordance with the open and close principle.
  2. The responsibilities are very clear and obvious.

Disadvantages:

  1. Some types of proxy patterns can slow down the processing of requests due to the addition of proxy objects between the client and the real subject.
  2. Implementing the proxy pattern requires additional work, and some of the proxy pattern implementations are quite complex.
  3. At the code level, if the interface changes, so does the proxy class.
A dynamic proxy

With the above foundation, let’s formally talk about dynamic proxy. As you can see from the above example, each proxy class can only implement one interface service. So if we create multiple proxy classes when there are multiple business types applicable to the proxy pattern in our software engineering, how do we solve this problem? In fact, our dynamic proxy was born.

It is obvious that the bytecode of the dynamic proxy class is dynamically generated by Java reflection at runtime, without requiring us to write its source code manually. Let’s take a look at the JDK dynamic proxy class based on the above example

JDK dynamic proxy

Take a direct look at the use of JDK dynamic proxies as shown in the following code block

public class JDKDynamicProxy implements InvocationHandler {

    // The proxied object
    private Object object;

    public JDKDynamicProxy(Object object) {
        this.object = object;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(object,args);
        return result;
    }

    // Generate the proxy class
    public Object createProxyObj(a){
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this); }}Copy the code

Static proxy, dynamic proxy calls are as follows:

public class TestProxy {
    public static void main(String[] args) {
        // Static proxy test
        RailwayStation railwayStation = new RailwayStation();
        railwayStation.getTicket();

        RailwayAgencyProxy railwayAgencyProxy = new RailwayAgencyProxy(railwayStation);
        railwayAgencyProxy.getTicket();

        // Dynamic proxy test
        Ticket ticket = new RailwayStation();
        JDKDynamicProxy jdkDynamicProxy = newJDKDynamicProxy(ticket); Ticket proxyBuyTicket = (Ticket) jdkDynamicProxy.createProxyObj(); proxyBuyTicket.getTicket(); }}Copy the code

The advantages of dynamic proxies become apparent when you look at the dynamic proxy and static proxy tests above. If we demonstrate another jubajie and GaocuiLanProxy scenario, is there no need to create GaocuiLanProxy, we just need to create proxy class through JDKDynamicProxy.

Note that the proxy.newProxyInstance () method takes three arguments:

  1. ClassLoader: specifies the ClassLoader used by the current target object. The method for obtaining the loader is fixed.
  2. Class
    [] interfaces: Specifies the type of the interface implemented by the target object, using generics to confirm the type
  3. InvocationHandler: Specifies the dynamic handler that fires the event handler method when executing the target object’s method

The JDK dynamic proxy is an interface oriented proxy model. To use a JDK proxy, you must first have an interface, such as the Ticket interface in the example above

Cglib dynamic proxy

Let’s look at the CGLIb dynamic proxy again. Let’s take a look at what Cglib is. There is little official explanation for CGLIb, but it is powerful, and many people complain about it. CGLIB(Code Generation Library) is an open-source project! Is a powerful, high-performance, high-quality code-generated library that extends Java classes and implements Java interfaces at run time. CGLIB is a powerful high-performance code generation package. It is widely used by many AOP frameworks, such as Spring AOP’s interception of methods for them. At the bottom of the CGLIB package is the ability to transform bytecode and generate new classes by using ASM, a small and fast bytecode processing framework. In addition to the CGLIB package, scripting languages such as Groovy and BeanShell also use ASM to generate Java bytecode. Direct use of ASM is discouraged, of course, because it requires that you be familiar with the JVM’s internal structure, including the class file format and instruction set.

Cglib does not come with the JDK, so if we are working on a Maven project, we will first need to introduce cglib related POM dependencies, as follows:

<dependency>    
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
Copy the code

Since the object of the Cglib proxy is a class, this is different from the JDK dynamic proxy, which is highlighted in red. In this case, our same proxy class should be defined as follows,

public class Ticket {
    public void getTicket(a){   
        System.out.println("Bought a train ticket.");  
    }   
    
     final public void refundTicket(a){  
        System.out.println("Returned a train ticket."); }}Copy the code

Obviously, the above class defines two methods, one is to buy a train ticket and the other is to refund a train ticket.

public class CglibDynamicProxy implements MethodInterceptor {    
        
    / * * *@paramO Proxy object * generated by cglib@paramMethod Method of the proxied object *@paramObjects The parameter * passed to the method@paramMethodProxy Proxy methods *@return
     * @throws Throwable
     */   
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("execute pre");
            Object obj = methodProxy.invokeSuper(o,objects);
            System.out.println("execute after");
             returnobj; }}Copy the code

Call the test entry method call

public class TestCglibProxy {
    public static void main(String[] args) {
        // The proxy class file is stored in the local disk to facilitate the decompression of the source code
   System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/kdaddy/project/log");
        // The process of obtaining proxy objects through CGLIB dynamic proxies
        Enhancer enhancer = new Enhancer();
        // Set the enhancer object's parent class
        enhancer.setSuperclass(Ticket.class);
        // Set the enhancer callback object
        enhancer.setCallback(new CglibDynamicProxy());
        // Create a proxy object
        Ticket ticket = (Ticket) enhancer.create();
        // Call the target method through the proxy object
        ticket.getTicket();
        // Call the target method by attempting to call the final object through the proxyticket.refundTicket(); }}Copy the code

After running, we get the following result:

To cancel a train ticketCopy the code

According to the printing situation of the log, it is easy to find that the relevant print “cancelled a train ticket” is not represented, so we can draw a conclusion that the Cglib dynamic agent cannot represent the method modified by the final.

In the source code above, we mentioned to write the proxy class to the relevant disk, open the corresponding directory, we will find the following three files

Ticket
E n h a n c e r B y C G L I B EnhancerByCGLIB
Class 4e79A04A is a proxy class generated by Cglib. This class is Ticket. Let’s take a look at the relevant source:

public class Ticket$$EnhancerByCGLIB$$4e79a04a extends Ticket implements Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    / / the interceptor
    private MethodInterceptor CGLIB$CALLBACK_0; 
    private static Object CGLIB$CALLBACK_FILTER;
    // proxy method
    private static final Method CGLIB$getTicket$0$Method; 
    // Proxy method
    private static final MethodProxy CGLIB$getTicket$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("kdaddy.com.cglibDynamic.Ticket$$EnhancerByCGLIB$$4e79a04a");
        Class var1;
        CGLIB$getTicket$0$Method = ReflectUtils.findMethods(new String[]{"getTicket"."()V"}, (var1 = Class.forName("kdaddy.com.cglibDynamic.Ticket")).getDeclaredMethods())[0];
        CGLIB$getTicket$0$Proxy = MethodProxy.create(var1, var0, "()V"."getTicket"."CGLIB$getTicket$0");
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals"."(Ljava/lang/Object;) Z"."toString"."()Ljava/lang/String;"."hashCode"."()I"."clone"."()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;) Z"."equals"."CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;"."toString"."CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I"."hashCode"."CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;"."clone"."CGLIB$clone$4"); }}Copy the code

As you can see from the source code of the proxy class, the proxy class gets all the methods inherited from the parent class, and has a MethodProxy corresponding to them, except for methods that are modified with final, and we certainly don’t see the refundTicket method in the source code above. So let’s move on.

Let’s look at a call to one of these methods.

// Proxy method (methodProxy.invokeSuper is called)
final void CGLIB$getTicket$0() {
        super.getTicket();
    }

Invoke methodProxy.invoke is called, which is why calling methodProxy.invoke from an interceptor loops in an infinite loop, calling the interceptor all the time.
    public final void getTicket(a) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 ! =null) {
             // Call the interceptor
            var10000.intercept(this, CGLIB$getTicket$0$Method, CGLIB$emptyArgs, CGLIB$getTicket$0$Proxy);
        } else {
            super.getTicket(); }}Copy the code

Let’s look at the entire call link of getTicket: Call getTicket – > () method call blocker – > methodProxy. InvokeSuper – > additional getTicketgetTicketgetTicket0 – > agent getTicket method.

Methodproxy. invokeSuper (methodProxy.invokeSuper)

public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { this.init(); MethodProxy.FastClassInfo fci = this.fastClassInfo; return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException var4) { throw var4.getTargetException(); Private static class FastClassInfo {FastClassInfo f1; // The proxied class FastClass f2; // Proxy class FastClass int i1; // Proxied class method signature (index) int i2; Private FastClassInfo() {}}Copy the code

The above code calls the FastClass of the proxy class and executes the proxy method. Remember generating three class files earlier? Ticket EnhancerByCGLIBEnhancerByCGLIBEnhancerByCGLIB4e79a04a FastClass FastClassByCGLIBFastClassByCGLIBFastClassByCGLIBf000183. Class is the proxy class, Ticket FastClassByCGLIBFastClassByCGLIBFastClassByCGLIBa79cabb2. Class is FastClass by proxy class.

The FastClass Cglib dynamic proxy executes the proxy method more efficiently than the JDK because Cglib uses FastClass. Generates a Class for the proxy Class and a proxied Class that assigns an index(int) to the proxy Class or proxied Class’s methods. With this index as an input parameter, FastClass can directly locate the method to be called, eliminating reflection calls, so calls are more efficient than JDK dynamic proxies using reflection calls. Let’s decompile a FastClass:

public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -80792013:
            if (var10000.equals("getTicket()V")) {
                return 0;
            }
            break;
        case 189620111:
            if (var10000.equals("cancelTicket()V")) {
                return 1;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;) Z")) {
                return 2;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 3;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 4; }}return -1; }... Some code omitted here...public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        Ticket var10000 = (Ticket)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.getTicket();
                return null;
            case 1:
                var10000.cancelTicket();
                return null;
            case 2:
                return new Boolean(var10000.equals(var3[0]));
            case 3:
                return var10000.toString();
            case 4:
                return newInteger(var10000.hashCode()); }}catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
    
Copy the code

FastClass is not generated with the proxy class, but is generated and cached the first time a MethodProxy Invoke /invokeSuper is executed.

// invoke/invokeSuper init()
private void init(a) {
        if(this.fastClassInfo == null) {
            Object var1 = this.initLock;
            synchronized(this.initLock) {
                if(this.fastClassInfo == null) {
                    MethodProxy.CreateInfo ci = this.createInfo;
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);// New FastClass is generated if it is not in the cache.
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);// Get the index of the method
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null; }}}}Copy the code

conclusion

If the screen in front of you like Zhang Xiaoshuai, how to deal with it? In fact, most of the answers are up there. JDK dynamic proxies are interfaces that implement proxied objects, and Cglib is interfaces that inherit proxied objects. (2) Both JDK and Cglib generate bytecodes at runtime. JDK writes Class bytecodes directly, while Cglib uses the ASM framework to write Class bytecodes. Cglib proxy implementation is more complex and less efficient than JDK generation proxy. (3) JDK call proxy method, is through reflection mechanism call, Cglib is through FastClass mechanism direct call method, Cglib execution efficiency is higher.

I am an old cat, more content, welcome you to search attention to the old cat public number “programmer old cat”.