JDK dynamic proxy of Java Dynamic Proxy Mechanism

Posted by Tyen on Thu, 03 Mar 2022 16:49:04 +0100

preface

Proxy Pattern is one of the 23 commonly used design patterns of object-oriented software. Its function is to provide a proxy for other objects to control the access to this object, that is, middleman or purchasing agent

For example, the proxy class can be used to implement the function of the request directly after calling the proxy class. For example, the proxy class can be used to implement the function of the request directly after calling the proxy class.

Why add a layer of proxy classes? There are two advantages:

  1. Hide and protect interface implementation classes and control direct access to interface implementation class objects

  2. New functions can be added to improve the scalability of the system

JDK dynamic proxy is one of the commonly used proxy mechanisms, which is mainly realized through reflection. To realize JDK dynamic proxy, the InvocationHandler interface must be implemented. At the same time, it will be required to rewrite the invoke() method, as shown in the parameter list below

  • Proxy: dynamic proxy class (the person who buys it for you)

  • Method: called method

  • args: parameter of the called method

public interface InvocationHandler {
 
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

practice

Steps for using JDK dynamic proxy

  1. Create the proxy interface and class;
  2. Create the implementation class of InvocationHandler interface and implement the proxy logic in the invoke method;
  3. Create a Proxy object through the Proxy's static method newproxyinstance (classloader, class [] interfaces, invocationhandler h)
  4. Use proxy objects.

Let's write a chestnut to show the calling process of dynamic proxy

The first is the interface of dynamic agent, which writes a method and overloads it

 
public interface TicketProvider {
    public void getTrainTicket();
    public void getTrainTicket(int count);
}

Then its implementation class

public class TicketProviderImpl implements TicketProvider {
 
    @Override
    public void getTrainTicket() {
        System.out.println("Buy train tickets");
    }
 
    @Override
    public void getTrainTicket(int count) {
        System.out.println("Buy train tickets"+count+"Zhang");
    }
}

Then there is the proxy class, which implements the InvocationHandler interface and rewrites the invoke method

public class TicketProviderProxy implements InvocationHandler {
    //The actual object, that is, the store to which the purchasing agent is going
    private Object target;
 
    //Constructor assignment
    public TicketProviderProxy(Object target) {
        this.target = target;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-----before in method invoke-----");
        //The corresponding method is found through the object and invoke()
        method.invoke(target, args);
        System.out.println("Add functionality to the method");
        System.out.println("-----after in method invoke-----");
        return null;
    }
}

Let's test it

public class ProxyTest {
    public static void main(String[] args) {
        //Interface implementation class
        TicketProvider ticketProvider = new TicketProviderImpl();
        //InvocationHandler implementation class uses the constructor just defined
        InvocationHandler handler = new TicketProviderProxy(ticketProvider);
        //newProxyInstance() generates a dynamic proxy class instance. args[0] is the implementation class and args[1] is the interface class to be,
        //args[2] is the InvocationHandler implementation class, that is, the processor, which processes the incoming real object that needs to be proxied
        TicketProvider ticketProviderProxy = (TicketProvider) Proxy.newProxyInstance(
                ticketProvider.getClass().getClassLoader(),
                ticketProvider.getClass().getInterfaces(),
                handler);
        //Incoming parameter test
        ticketProviderProxy.getTrainTicket();
        ticketProviderProxy.getTrainTicket(5);
    }
}

You can see that the output is

It can be seen that the proxy object created through the newProxyInstance() method can call the method of the interface implementation class, and the added method is also output. In this way, the dynamic agent is realized.

Source code

Proxy. newProxyInstance (classloaderloader, class [] interfaces, invocationhandler h) generates proxy objects, so we go to the implementation of newProxyInstance:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        // Inspection h is not empty
        Objects.requireNonNull(h);
        // Copy class object of interface
        final Class<?>[] intfs = interfaces.clone();
        // Do some security checks
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         *  Query (already exists in the cache) or generate the class object of the specified proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            // Get the constructor of the proxy class object. The parameters of this constructor are specified by constructorparams, and the parameters constructorParames are constant values: private static final class <? > [] constructorParams = { InvocationHandler.class };
            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;
                    }
                });
            }
            // Generate proxy object. The passed in parameter new Object[]{h} will be described later
            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 core of this code is to get the Class object of the proxy Class through getProxyClass0(loader, intfs), then get the construction method through the Class object, and then create the proxy object.

The use of reflection lies in the generation of the dynamic proxy class proxy, that is, the newProxyInstance() method. It implements the second parameter, that is, the interface class. Then, inside the interface method, it calls the invoke() method of the handler through reflection, and the invoke() method passes the internal method invoke() checks the parameters passed in, and then calls the methods of the interface implementation class according to the different parameters.

The implementation of this point in spring AOP is the same, but the dynamic proxy mechanism of AOP has more cglib dynamic proxy, which is used to proxy objects that do not implement interfaces. Beans like this are represented by cglib.

Scan code concerns my WeChat official account: Java architects advanced programming
Get the latest interview questions and share the dry goods of Java technology, including JVM, SpringBoot, SpringCloud, database, architecture design, interview questions, e-books, etc. we look forward to your attention!

Topics: Java