Java static proxy and dynamic proxy

Java static proxy and dynamic proxy

@author: Jingdai
@date: 2021.05.03

proxy pattern

Provide (wrap) a proxy for the target object, which can control access to the target object.

  • Call the target object through the proxy object
  • Proxy objects can add monitoring and review processing

Static proxy

Both the proxy class and the target object should implement the same interface. At the same time, the proxy object needs to hold the target object. When the outside world needs to call the target object, the proxy object should be called directly. At the same time, some pre-processing and post-processing can be added when the proxy object is called. Here is an example.

Interface

package proxy;

// interface
public interface Subject {
    
    void doRequest();
}

Target object

package proxy;

public class SubjectImpl implements Subject {
    
    @Override
    public void doRequest() {
        System.out.println("Real Do Request!");
    }
}

Proxy object

package proxy;

public class StaticProxy implements Subject {

    Subject subject;

    public StaticProxy(Subject subject) {
        this.subject = subject;
    }
    
    @Override
    public void doRequest() {
        System.out.println("Proxy pre process!");
        subject.doRequest();
        System.out.println("Proxy post process!");
    }
}

Test class

package proxy;

public class Test {

    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        StaticProxy staticProxy = new StaticProxy(subject);
        staticProxy.doRequest();
    }

}

output

Proxy pre process!
Real Do Request!
Proxy post process!

As can be seen from the above example, the implementation of static proxy is simple and easy to understand. As long as you pay attention to the need to add a target object attribute to the proxy object. However, static proxy also has disadvantages. For example, if a method is added to the interface, not only the target object needs to add a method implementation, but also the static proxy object needs to add a method implementation, which is very cumbersome. Thus, there is a dynamic agent.

JDK dynamic agent

Dynamic agents are more complex than static agents. See the figure below. (this refers to the dynamic agent of JDK, not CGlib)

The agent processor here is similar to the agent object in the static agent. It has the target object, and it completes the call, pre-processing and post-processing of the target object. Java implements a proxy processor through an InvocationHandler interface. In this interface, there is an invoke() method. Its formal parameter method refers to the method of the target object, which is used to call the method of the target object.

After the InvocationHandler is defined, a Proxy object is automatically created through the static method of the Proxy class, and then the Proxy function is completed through the automatically created Proxy object. Look at the following example. Note that the interface and target object here are the same as static Proxy.

Interface

package proxy;

// interface
public interface Subject {
    
    void doRequest();
}

Target object

package proxy;

public class SubjectImpl implements Subject {
    
    @Override
    public void doRequest() {
        System.out.println("Real Do Request!");
    }
}

Agent processor

package proxy;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;

public class ProxyHandler implements InvocationHandler{

    public Subject subject;

    public ProxyHandler(Subject subject) {
        this.subject = subject;
    }

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

        System.out.println("Handler pre process!");
        Object object = method.invoke(subject, args);
        System.out.println("Handler post process!");
        return object;
    }
}

Test class

package proxy;

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

public class Test {

    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler handler = new ProxyHandler(subject);
        Subject dynamicProxy = (Subject) Proxy.newProxyInstance(SubjectImpl.class.getClassLoader(), 
            SubjectImpl.class.getInterfaces(), handler);
        dynamicProxy.doRequest();
    }

}

output

Handler pre process!
Real Do Request!
Handler post process!

It can be seen here that the Proxy objects of dynamic Proxy are automatically generated in Java, not implemented by defining classes. At the same time, the classes of these Proxy objects are inherited from the Proxy class.

It should be noted here that if there are multiple methods in the target object of the agent, the multiple methods will be processed through the invoke() method of InvocationHandler, and the pre-processing and post-processing of multiple methods will become the same. How can different methods be processed differently? Very simply, you can judge the method parameter and args parameter of the invoke() method and make different processing according to different method names and call parameters. See the following example.

Interface

package proxy;

// interface
public interface Subject {
    
    void doRequest();

    void doAnotherRequest();
}

Target object

package proxy;

public class SubjectImpl implements Subject {
    
    @Override
    public void doRequest() {
        System.out.println("Real Do Request!");
    }

    @Override
    public void doAnotherRequest() {
        System.out.println("Real Do Another Request!");
    }
}

Agent processor

package proxy;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;

public class ProxyHandler implements InvocationHandler{

    public Subject subject;

    public ProxyHandler(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
        throws IllegalAccessException, InvocationTargetException {
        
        String methodName = method.getName();
        Object object = null;

        if ("doRequest".equals(methodName)) {
            System.out.println("Handler pre process doRequest!");
            object = method.invoke(subject, args);
            System.out.println("Handler post process doRequest!");
        } else if ("doAnotherRequest".equals(methodName)) {
            System.out.println("Handler pre process doAnotherRequest!");
            object = method.invoke(subject, args);
            System.out.println("Handler post process doAnotherRequest!");
        }
        return object;
    }
}

Test class

package proxy;

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

public class Test {

    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler handler = new ProxyHandler(subject);
        Subject dynamicProxy = (Subject) Proxy.newProxyInstance(SubjectImpl.class.getClassLoader(), 
            SubjectImpl.class.getInterfaces(), handler);
        dynamicProxy.doRequest();
        dynamicProxy.doAnotherRequest();
    }

}

output

Handler pre process doRequest!
Real Do Request!
Handler post process doRequest!
Handler pre process doAnotherRequest!
Real Do Another Request!
Handler post process doAnotherRequest!

reference resources

  • MOOC Java core technology (Advanced) East China Normal University (Chen Liangyu)

Keywords: Java Proxy

Added by trexx on Fri, 18 Feb 2022 05:38:36 +0200