In depth analysis of static and dynamic proxies in Java

  • 2020-04-01 02:18:42
  • OfStack

Proxy is often used in Java coding. Proxy is divided into static proxy and dynamic proxy. Where the dynamic proxy can implement aop in spring.

I. static agent: Before the program runs, the programmer writes the proxy and then compiles it, that is, the bytecode file of the proxy class is generated before the program runs

The public parent of the proxied class


package staticproxy;
public abstract class BaseClass {
    public abstract void add();
}

By the proxy class

package staticproxy;
public class A extends BaseClass {
    public void add() {
        System.out.println("A add !");
    }
}

The proxy class

package staticproxy;
public class Proxy {
    BaseClass baseClass;
    public void add() {
        baseClass.add();
    }
    public void setBaseClass(BaseClass baseClass) {
        this.baseClass = baseClass;
    }
    public static void main(String[] args) {
        BaseClass baseClass = new A();
        Proxy proxy = new Proxy();
        proxy.setBaseClass(baseClass);
        proxy.add();
    }
}

Ii. Dynamic agency The actual code is not generated at compile time, but is generated dynamically at run time using reflection

Interface of the proxied class


package jdkproxy;
public interface Service {
    public void add();
    public void update();
}

Proxied class A

package jdkproxy;
public class AService implements Service {
    public void add() {
        System.out.println("AService add>>>>>>>>>>>>>>>>>>");
    }
    public void update() {
        System.out.println("AService update>>>>>>>>>>>>>>>");
    }
}

Proxied class B

package jdkproxy;
public class BService implements Service {
    public void add() {
        System.out.println("BService add---------------");
    }
    public void update() {
        System.out.println("BService update---------------");
    }
}

The proxy class

package jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    MyInvocationHandler() {
        super();
    }
    MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //Add logic before program execution
        System.out.println("before-----------------------------");
        //Program execution
        Object result = method.invoke(target, args);
        //Add logic after program execution
        System.out.println("after------------------------------");
        return result;
    }
    public Object getTarget() {
        return target;
    }
    public void setTarget(Object target) {
        this.target = target;
    }
}

The test class

package jdkproxy;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) {
        Service aService = new AService();
        MyInvocationHandler handler = new MyInvocationHandler(aService);
        //Proxy dynamically creates a Proxy instance that conforms to an interface for the InvocationHandler implementation class
        Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
                .getClass().getClassLoader(), aService.getClass()
                .getInterfaces(), handler);
        //Dynamically generated proxy object to aServiceProxy agent to execute the program, where aServiceProxy conforms to the Service interface
        aServiceProxy.add();
        System.out.println();
        aServiceProxy.update();
        //The following is the agent for B
        // Service bService = new BService();
        // MyInvocationHandler handler = new MyInvocationHandler(bService);
        // Service bServiceProxy = (Service) Proxy.newProxyInstance(bService
        // .getClass().getClassLoader(), bService.getClass()
        // .getInterfaces(), handler);
        // bServiceProxy.add();
        // System.out.println();
        // bServiceProxy.update();
    }
}

Output results:
Before -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
AService add> > > > > > > > > > > > > > > > > >
After -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Before -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
AService update> > > > > > > > > > > > > > >
After -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

With the above standard red statement is the key to produce the proxy class code, can produce a proxy object, in line with the Service interface newProxyInstance this method would do such a thing, he will take you to all the interface agent, with a class that is dynamically generated by the code to do this, all the interface methods in the class rewritten as call InvocationHandler. Invoke () method.

How is the proxy object generation implemented in detail

The newProxyInstance method of the Proxy, where the exception handling statements in the method have been truncated for convenience

Public static Object newProxyInstance(ClassLoader loader,  Class< ? > [] interfaces, throws InvocationHandler h)


    public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException  
    {  
        if (h == null) {  
            throw new NullPointerException();  
        }  
        //Generates the specified proxy class
        Class cl = getProxyClass(loader, interfaces);  
        Constructor cons = cl.getConstructor(constructorParams);  
        //Generate an instance of the proxy class and pass an instance of MyInvocationHandler to its constructor. The proxy class object will call the invoke method of MyInvocationHandler when it is actually executed.
        return (Object) cons.newInstance(new Object[] { h });  
    }   Among them getProxyClass Method returns an instance of the proxy class 

Proxy's getProxyClass method

public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
{
    //Much of the caching, exception handling, and judgment logic code has been omitted to make the program more prominent
    byte[] proxyClassFile =    ProxyGenerator.generateProxyClass(proxyName, interfaces);
    proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
    proxyClasses.put(proxyClass, null);
    return proxyClass;
}

Next, look at the generateProxyClass method of ProxyGenerator, which finally produces the bytecode file of the proxy class:

public static byte[] generateProxyClass(final String name, Class[] interfaces)  
   {  
       ProxyGenerator gen = new ProxyGenerator(name, interfaces);  
    //Here, the bytecode of the proxy class is dynamically generated
       final byte[] classFile = gen.generateClassFile();  
    //If the value of saveGeneratedFiles is true, the bytecode of the generated proxy class is saved to the hard disk & PI.
       if (saveGeneratedFiles) {  
           java.security.AccessController.doPrivileged(  
           new java.security.PrivilegedAction<Void>() {  
               public Void run() {  
                   try {  
                       FileOutputStream file =  
                           new FileOutputStream(dotToSlash(name) + ".class");  
                       file.write(classFile);  
                       file.close();  
                       return null;  
                   } catch (IOException e) {  
                       throw new InternalError(  
                           "I/O exception saving generated file: " + e);  
                   }  
               }  
           });  
       }  
    //Returns the bytecode & NBSP of the proxy class;
       return classFile;  
   }

So what does the resulting proxy class look like, as follows (omits equals, hashcode, toString, etc., showing only the constructor and add methods) :

public final class $Proxy11 extends Proxy implements Service  
{      //Constructor, the argument is an instance of the MyInvocationHandler class that was just passed over.
    public $Proxy11(InvocationHandler invocationhandler)  
    {  
        super(invocationhandler);  
    }  

      
    public final void add()  
    {  
        try  
        {  
            //It's really just calling the invoke method & NBSP; in MyInvocationHandler;
            super.h.invoke(this, m3, null);  
            return;  
        }  
        catch(Error _ex) { }  
       catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
   }  
}  


Related articles: