In-depth dynamic proxy source

Introduction: Early Learning dynamic proxy usage scenarios and use in the actual development, we also know that the most classic mybatis the mapper is the use of dynamic proxy to achieve, then the dynamic behind the proxy What principle? Why can achieve dynamic proxy? Why dynamic proxy can only proxy interfaces, and can not resell the general category? Why dynamic proxy need classLoder and interfaces incoming class? With these questions, we turned to the theme of this issue: to explore the inner workings of dynamic proxies.

This blog directories

One: the basics of using dynamic proxy

Two: the internal operation of the process of dynamic proxies

Three: several related issues

Four: summary

One: the basics of using dynamic proxy

 1.1: Simple example

First, we simulate a simple process of dynamic proxy: a singer go to a party, you need to sing, he needed someone to play the announcer in the process: start playing, playing end, each singer will follow this process, the singer process performances, interspersed with host prologue and epilogue, we use the code to simulate this scenario:

1.2: Proxy Interface

First, we define a singer interface represents us to proxy interfaces:

public  the interface Singer {
     / ** 
     *表演
     * @param soonName
      * / 
    public  void perform, (String soonName),; 
}

1.3: interface implementation class

public  class Jay the implements Singer, { 
  
    public  void the perform (String soonName) { 
        System.out.println ( "Then I sing for everyone" + soonName); 
    } 
}

1.4: Auxiliary class used to simulate the lines before and after the registrant

public  class Presenter { 

    public  void the before () { 
        System.out.println ( "Please proceed with your show!" ); 
    } 

    public  void the After () { 
        System.out.println ( "end of the show, everyone applauded!" ); 
    } 
}

1.5: Specific proxy class

  Proxy.newProxyInstance used here to create a proxy class, passing the original class and the interface class loader interface of InvocationHandler, while before and after insertion Presenter class methods for pre- and post-treatment

public class SingerProxy {

   private Presenter presenter;

   public SingerProxy(Presenter presenter){
       this.presenter = presenter;
   }
    /**
     * 获取代理对象
     * @return
     */
    public Singer getProxy(){

        final Singer jay = new Jay();
        Singer singerProxy = (Singer)Proxy.newProxyInstance(jay.getClass().getClassLoader(), jay.getClass().getInterfaces(), new InvocationHandler() {」
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                presenter.before();
                method.invoke(jay, args);
                presenter.after();
                return null;
            }
        });
        return singerProxy;
    }
}

1.6: Test class

public class Test {
    public static void main(String[] args) {
        SingerProxy singerProxy = new SingerProxy(new Presenter());
        Singer proxy = singerProxy.getProxy();
        proxy.perform("《夜曲》");
    }
}

Output:

 II: Dynamic proxy internal inquiry

As can be seen from the above example we first generate a proxy class, then the proxy class to call the original interface method, we can achieve a predetermined logic (or when abnormality occurs) before and after the insert our original interface want logic, then what is why?

2.1: Find the generated proxy class

If we need to open the generated classes, you first need to add this line of code in the test class, set the system property to save the generated proxy class class file:

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

2.2: singerProxy class

Generated by the dynamic proxy proxy class name: $ Proxy0.class then after intelj idea is to decompile the source code, where there are four main methods see, method of m1 \ m2 \ m3 \ m0; are respectively obtained by reflecting the equals (), toString (), perform (), hashcode () method, while the proxy class inherits the proxy interfaces and to achieve the original Singer, rewritten perform () method, which explains why so classes can call the agent perform () method, perform the method in turn calls a method InvoationHander invoke the parent class, and passed to the method of the original interface, and invoke methods rewritten when we create a proxy class, so we will follow since call invoke methods defined logic, executed in the order

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import main.learn.proxy.Singer;

public final class $Proxy0 extends Proxy implements Singer {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void perform(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("main.learn.proxy.Singer").getMethod("perform", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 2.3: proxy class generation process

We figured out above, will automatically inherit the original interface classes in the proxy class and calls the interface method InvocationHandler will pass into the class, then this class is how to generate it? This would turn generate a proxy class source code:

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

The above code is interpreted by the down:

2.3.1: security manager checks with Agent Permission Checks

2.3.2: determining the length of the interface is greater than 65,535, greater than not proceed. Then get WeakCache proxy classes from the cache, if you can not find the proxy class generated by proxyClassFactory

2.3.2.1: class proxy class generating process as follows:

1⃣️ verify that incoming interface load may be passed classloader

2⃣️ verify whether it is passed in an interface, if the interface is not a direct throw an IllegalArgumentException

3⃣️ determine whether incoming repeated here by a IdentityHashMap entered, put class class interface, if the return value is not null to this interface has already been registered (if for the first time put returns null, according to the traditional approach is to first get whether it is null, if not null then put, this line is wonderful eliminates this step), IdentityHashMap is a map, but it is our common HashMap biggest difference is that it does not by equals and hashCode The method is repeated to judge whether the key, but by operator ==

4⃣️ splicing fixed proxy class name:. Com.sun.proxy $ Proxy + atom number increment, to prevent concurrent calls, when generating the name of the proxy class, using AtomicLong getAndIncrement method of increasing sequence numbers to atoms proxy class, this method is atomic, so no concurrency issues. Here it is why we see the final proxy class is $ Proxy0 reason of (a number generated proxy class is starting from 0)

5⃣️ call ProxyGenerator.generateProxyClass method to generate the proxy class category (process is more complicated, with some jvm bytecode instruction to generate, the method including traversing the type of the return value, parameters, type, etc.)

6⃣️ defineClass previous step by the generated class bytecode class files generated, which is a native method, it is necessary to perform classloader incoming class bytecode for a class is loaded and then generate the generated class

2.3.3: Creating a reflection instance constructor constractor get through reflection, this process was forced constructor private reflection privatization

Three: several related issues

 3.1: Why Dynamic Proxy need to pass classLoader?

  The main reasons are the following:

1⃣️ need to verify whether the incoming interface can be loaded current class loader can not load if to prove this interface with the class loader is not the same, according to the parent delegation model, then the class loader hierarchy is broken

2⃣️ class loader needs to generate bytecode classes according to the class generated by the method defineClass class files, i.e. no class is not loaded, then the generated proxy class

3.2: Why you need to pass in a dynamic proxy interface and the only agent interface?

 Interface is required to be generated by the proxy class bytecode class ProxyGenerator, during generation, the method requires a traversal of the interface, including the method signature, parameter types, the return type to generate a new proxy class, the proxy class may also need to inherit the method of the original interface, so the interface must be passed

3.3: If you create an interface with multiple proxy how would you do?

First, the proxy object will get the time from the cache ( WeakCache inside take), if only to create did not get through the proxy factory, so if you create multiple proxy class, it will eventually generate a proxy class

 

 

Four: summary

    This blog is analyzed by a dynamic proxy practical examples of the specific process to create a dynamic proxy, analyzes the operating principle of the internal dynamic proxy, as well as analysis of the source code generated proxy class, dynamic proxies in our development process can be described as very common, such as the most typical mapper proxy principle of mybatis, spring aop realization of the principle, carry out pre-enhancement, post-enhancement is to make use of dynamic proxy. We understand the dynamic proxy can help us understand some source, perhaps in the future we can be certain scenes using dynamic proxy to create the appropriate wheels.

 

Guess you like

Origin www.cnblogs.com/wyq178/p/11514343.html