A list,

1.1 define

The proxy pattern is one of structural design patterns that provides an object with a proxy that controls access to the real object

Detailed explanation of proxy mode

According to the creation time of bytecode files, they can be classified into static proxies and dynamic proxies

1.2 Static Proxy

The bytecode file of the proxy class exists before the program runs, and the relationship between the proxy class and the target class is determined before the program runs

1.3 Dynamic Proxy

Proxy classes are generated dynamically by the JVM during program execution based on mechanisms such as reflection, and there are no bytecode files of proxy classes before the program runs

Why dynamic proxies?

Static proxies are simple to implement and can extend the functionality of the target class without invading the original code. However, when the scenario is slightly more complex, static proxies have the following disadvantages:

1, When you need to proxy multiple classes, there are two ways for the proxy class to implement the same interface as the target class (here refers to the target class to implement different interfaces, if you need to extend the target class to implement the same interface can choose to use the decorator pattern).

  • Maintaining only one proxy class implements all the target class interfaces, but this can result in the proxy class becoming too large
  • Maintaining multiple proxy classes, one for each target object, can lead to too many proxy classes

2. When an interface needs to be added, deleted, or modified, both the target class and the proxy class need to be modified at the same time, which is difficult to maintain

Dynamic proxies can address the disadvantages of static proxies

Ii. Implementation methods and principles

2.1 JDK Dynamic Proxy

This method is implemented through the interface

The JDK dynamic Proxy design two main categories: Java. Lang. Reflect. The Proxy and Java lang. Reflect. InvocationHandler

2.1.1 Implementation Example

  • Target class interface:
public interface TargetInterface {

    /** * original method 1 **@return String
     */
    String doSomething(a);

    /** * original method 2 **@return int
     */
    int printNum(a);
}
Copy the code
  • The target class:
public class Target implements TargetInterface {

    @Override
    public String doSomething(a) {
        System.out.println("doSomething");
        return "doSomething";
    }

    @Override
    public int printNum(a) {
        System.out.println("printNum");
        return 100; }}Copy the code
  • Proxy class logic handling :(not a real proxy class)
public class DynamicProxyHandler implements InvocationHandler {

    /** * Target object */
    private final Object target;

    public DynamicProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("do something before");
        Object invoke = method.invoke(target, args);
        System.out.println("do something after");
        returninvoke; }}Copy the code
  • Dynamic proxy object generation:
mport java.lang.reflect.Proxy;

public class ProxyTest {

    public static void main(String[] args) {
        // create a proxied target object
        Target target = new Target();
        // create a proxy class handler object
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(target);
        // create a proxy object
        // a.jdk dynamically creates bytecode in memory equivalent to the. Class file from the passed parameter information
        //b. It will then be converted to the corresponding class according to the corresponding bytecode
        //c. Finally create an instance of the proxy class
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), dynamicProxyHandler);

        System.out.println("doSomething() call: " + proxy.doSomething());
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
        System.out.println("proxy.printNum() call: "+ proxy.printNum()); }}Copy the code
  • Output result:
do something before
doSomething
do something after
doSomething(a) call: doSomething
------------------------
do something before
printNum
do something after
proxy.printNum(a) call: 100
Copy the code

2.1.2 Mechanism

  • Specific steps
  1. By implementingInvovationHandlerThe interface creates its own call handler
  2. By providingProxyClass specifies a ClassLoader object and a set of interfaces to create a dynamic proxy class
  3. Gets the constructor of the dynamic proxy class through reflection, whose only argument type isInvocationHandlerThe interface type
  4. Constructor to create a dynamic proxy class instance that calls the handler object (InvocationHandlerThe implementation class instance of the interface) is passed in as an argument
  • ProxyA kind of analysis
public class Proxy implements java.io.Serializable {

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
        
        finalClass<? >[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();
        if(sm ! =null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /* * Finds or generates the specified proxy class */Class<? > cl = getProxyClass0(loader, intfs);/* * Gets the constructor object */ that calls the handler interface
        try {
            if(sm ! =null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            finalConstructor<? > cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
            /* * If the scope of the Class is private, set setAccessible */
            if(! Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run(a) {
                        cons.setAccessible(true);
                        return null; }}); }/* * Create a proxy class instance */
            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 newInternalError(t.toString(), t); }}catch (NoSuchMethodException e) {
            throw newInternalError(e.toString(), e); }}}/** * Generate the proxy class */
private staticClass<? > getProxyClass0(ClassLoader loader, Class<? >... interfaces) {if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        If the proxy class implementing the specified interface exists in the specified loader, the cache copy is returned directly
        // Otherwise create a proxy class using ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }
Copy the code
  • ProxyClassFactoryClass generates proxy classes

The simple step is to class the bytecode file and generate the bytecode file using the Proxy class defineClass0 local method. Specific implementation code can be viewed Proxy class static internal class ProxyClassFactory source.

  • Bytecode file

System.getProperties().setProperty(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”); Method to modify a system variable that can save the generated bytecode file

Bytecode files:

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements TargetInterface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    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 doSomething() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    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 int printNum() throws  {
        try {
            return (Integer)super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    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);
Copy the code
  • The Proxy class inherits the Proxy class and implements the target class interface (in this case, the correspondingTargetInterfaceInterface), override equals, hashCode, toString methods
  • Classes and methods are public final, so they can only be used, not inherited or overridden
  • The implementation of the method eventually invokes the proxy invocation handler object (in this case, the correspondingDynamicProxyHandlerClass instance)

2.2 CGLIB dynamic proxy

CGLIB dynamic proxy is implemented based on ASM mechanism by generating subclasses of business classes as proxy classes.

Please refer to the following article for detailed usage introduction

Java dynamic proxy in detail

Third, refer to the article

JAVA Dynamic Proxy JAVA dynamic proxy details