preface

Proxy Pattern is one of the 23 commonly used design patterns of object-oriented software. The function of Proxy Pattern is to provide a Proxy for other objects to control the access to this object. Frankly speaking, it is a middleman or a daigou

As shown in the figure, the client sends a request to the interface. Normally, the impL of the interface implementation class is used to call the method to complete the request. However, with the addition of the proxy class, the method of the interface implementation class can be directly called by the proxy instance, and additional functions can be added.

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 most commonly used proxy mechanisms, mainly implemented through reflection. To implement JDK dynamic proxy, you must implement the InvocationHandler interface, which requires overriding the invoke() method, as shown in the parameter list below

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

  • Method: indicates the method to be called

  • Args: Arguments to the method being called

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

practice

Use the JDK dynamic proxy step

  1. Create proxied interfaces and classes;
  2. Create an implementation class for the InvocationHandler interface that implements the proxy logic in the Invoke method;
  3. Create a Proxy object through the Proxy’s static method newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)
  4. Use proxy objects.

Let’s write an example to show the dynamic proxy call process

The first is the dynamic proxy interface, which writes a method and overrides it

 
public interface TicketProvider {
    public void getTrainTicket(a);
    public void getTrainTicket(int count);
}
Copy the code

Then there’s its implementation class

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

Then there is the proxy class, which implements the InvocationHandler interface and overrides the Invoke method

public class TicketProviderProxy implements InvocationHandler {
    // The actual object, that is, the shop that the daigou go to
    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 invoke() method finds the corresponding method by passing the actual object and parameters
        method.invoke(target, args);
        System.out.println("Add functionality to the method");
        System.out.println("-----after in method invoke-----");
        return null; }}Copy the code

Let’s test the proxy

public class ProxyTest {
    public static void main(String[] args) {
        // Interface implementation class
        TicketProvider ticketProvider = new TicketProviderImpl();
        //InvocationHandler implements the class using the constructor just defined
        InvocationHandler handler = new TicketProviderProxy(ticketProvider);
        //newProxyInstance () generates an instance of the dynamic proxy class, args[0] is the implementation class, args[1] is the interface class,
        // Args [2] is the InvocationHandler implementation class, that is, the handler, which processes incoming real objects that need to be propped
        TicketProvider ticketProviderProxy = (TicketProvider) Proxy.newProxyInstance(
                ticketProvider.getClass().getClassLoader(),
                ticketProvider.getClass().getInterfaces(),
                handler);
        // Pass in a parameter test
        ticketProviderProxy.getTrainTicket();
        ticketProviderProxy.getTrainTicket(5); }}Copy the code

You can see that the output is

As you can see, the proxy object created with the newProxyInstance() method can call the methods of the interface implementation class and also output the added methods. Dynamic proxy is then implemented.

The source code

Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler H) generates the Proxy object, so we move on to the implementation of newProxyInstance:

public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h)
        throws IllegalArgumentException
    {
        // check that h is not null
        Objects.requireNonNull(h);
        // Copy the interface's class object
        finalClass<? >[] intfs = interfaces.clone();// Perform some security checks
        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);
            }
            // Get the constructor of the proxy object. The constructor takes the parameters specified by constructorParams and takes the constants: private static final Class
      [] constructorParams = { InvocationHandler.class };
            finalConstructor<? > cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
            if(! Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run(a) {
                        cons.setAccessible(true);
                        return null; }}); }New Object[]{h}
            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); }}Copy the code

GetProxyClass0 (loader, INTFS) to get the Class object of the proxy Class, and then get the constructor from the Class object, and then create the proxy object.

The use of reflection is in the generation of the dynamic proxy class, the newProxyInstance() method, which implements the second argument, the interface class. Inside the implementation of the interface method, the invoke() method of the handler is then invoked by reflection. The invoke() method validates the arguments passed through its internal method.invoke() method and then invokes the methods of the interface implementation class based on the arguments.

The same is true of SpringAOP, except that cglib dynamic proxies are used to proxy objects that do not implement interfaces. Beans, for example, are proxied using Cglib.

My wechat official number: Java Architect Advanced programming focus on sharing Java technology dry products, including JVM, SpringBoot, SpringCloud, database, architecture design, interview questions, e-books, etc., looking forward to your attention!