The proxy pattern is one of the common design patterns that is intended to provide a proxy for a given object to control access to that object. Java proxy is divided into dynamic proxy and static proxy, dynamic proxy is widely used in Java, such as Spring AOP implementation, remote RPC call and so on. The biggest difference between static and dynamic proxies is whether the proxy classes are generated before or after the JVM starts. This article will introduce Java static proxy and dynamic proxy, and the comparison between the two, focusing on the principle of dynamic proxy and its implementation.

The proxy pattern

Definition of proxy pattern: Provides a proxy for other objects to control access to that object. In some cases, an object is inappropriate or cannot directly reference another object, and a proxy object can act as an intermediary between the client and the target object. For example, the object to be accessed is on a remote machine. In object-oriented systems, some object for some reason (such as the object creation overhead is very large, or some operations require safety control, or need to access) outside the process, will have direct access to caused a lot of trouble for the user or the system structure, during a visit to this object we can add an object to this access layer.

Composition of agency

The agent consists of the following three roles:

  • Abstract roles: Business methods implemented by real roles are declared through interfaces or abstract classes.
  • Agent role: Implements abstract role, is the agent of real role, implements abstract method through the business logic method of real role, and can attach its own operations.
  • Real role: Implements the abstract role and defines the business logic to be implemented by the real role for the proxy role to invoke.

Advantages of agency

  1. Clear responsibilities: The real role is to implement the actual business logic, not the non-business logic (such as transaction management).
  2. Isolation: The proxy object acts as an intermediary between the client and the target object. The target object is not directly exposed to the client, thus isolating the target object
  3. High extensibility: Proxy objects can be flexibly extended to target objects.

Examples of agents

To illustrate how the agent works, let’s use an example of loading and displaying images. Images stored on disk will take a lot of time per IO. If we need to display images frequently, it will take a long time to read images from disk each time. We use a proxy to cache the image, only the first time the image is read from disk, then read from the cache, the source code example is as follows:

import java.util.*;
 
interface Image {
    public void displayImage(a);
}

//on System A 
class RealImage implements Image {
    private String filename;
    public RealImage(String filename) { 
        this.filename = filename;
        loadImageFromDisk();
    }

    private void loadImageFromDisk(a) {
        System.out.println("Loading " + filename);
    }

    public void displayImage(a) { 
        System.out.println("Displaying "+ filename); }}//on System B 
class ProxyImage implements Image {
    private String filename;
    private Image image;
 
    public ProxyImage(String filename) { 
        this.filename = filename; 
    }
    public void displayImage(a) {
        if(image == null)
              image = newRealImage(filename); image.displayImage(); }}class ProxyExample {
    public static void main(String[] args) {
        Image image1 = new ProxyImage("HiRes_10MB_Photo1");
        Image image2 = new ProxyImage("HiRes_10MB_Photo2");     
        
        image1.displayImage(); // loading necessary
        image2.displayImage(); // loading necessary}}Copy the code

Static agent

Static proxies need to define two classes in the program: the target object class and the proxy object class. To ensure the consistency of their behavior, the target object and the proxy object implement the same interface. The proxy class information is determined before the program runs, and the proxy object contains a reference to the target object.

To illustrate the use of static proxies: Suppose we have an interface method for calculating employee wages and an implementation class that implements specific logic. What if we need to log the logic for calculating employee wages? Adding directly to the implementation logic for calculating wages leads to the introduction of non-business logic, which does not conform to the specification. At this point we can introduce a logging agent that outputs the relevant log information before and after the payroll calculation.

  • The interface for calculating employee wages is defined as follows:
public interface Employee {
    double calculateSalary(int id);
}
Copy the code
  • The implementation class for calculating employee wages is as follows:
public class EmployeeImpl {
    public double calculateSalary(int id){
        return 100; }}Copy the code
  • The proxy class with logging is implemented as follows:
public class EmployeeLogProxy implements Employee {

    // The proxy class needs to contain an object reference to the target class
    private EmployeeImpl employee;

    // And provides a constructor with a parameter to specify which object to proide
    public EmployeeProxyImpl(EmployeeImpl employee){
        this.employee = employee;
    }

    public double calculateSalary(int id) {

        // Log before calling the calculateSalary method of the target class
        System.out.println("Currently counting employees:" + id + "Take home pay");
        double salary = employee.calculateSalary(id);
        System.out.println("Counting employees:" + id + "End of pay after tax.");
        // Log after calling the target class method
        returnsalary; }}Copy the code

A dynamic proxy

The main difference is that the proxy object classes for dynamic proxies are created while the program is running, while the static proxy object classes are determined at compile time. The advantage of dynamic proxy is that it does not require the developer to write many proxy classes manually. For example, in the example above, if another Manager class needs to log the payroll logic, then we need to create a ManagerLogProxy to proxy objects. If there are many objects to proxy, there will be many proxy classes to write.

This is not the case with dynamic proxies, where one type of proxy needs to be written once and can be applied to all proxy objects. For example, Employee and Manager in the previous example only need to abstract a salary-related interface to implement the agent using the same set of dynamic agent logic.

Dynamic proxy example

Let’s use the Employee and Manager salary calculation logic above to demonstrate the use of dynamic proxies.

Interface abstraction

We know that both Employee and Manager have logic to calculate salary and need to log the logic to calculate salary, so we need to abstract an interface to calculate salary:

public interface SalaryCalculator {
    double calculateSalary(int id);
}
Copy the code

Interface implementation

public class EmployeeSalaryCalculator implements SalaryCalculator{
    public double calculateSalary(int id){
        return 100; }}Copy the code
public class ManagerSalaryCalculator implements SalaryCalculator{
    public double calculateSalary(int id){
        return 1000000; }}Copy the code

Create the InvocationHandler for the dynamic proxy

public class SalaryLogProxy implements InvocationHandler {
    private SalaryCalculator calculator;

    public SalaryLogProxy(SalaryCalculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("--------------begin-------------");
        Object invoke = method.invoke(subject, args);
        System.out.println("--------------end-------------");
        returninvoke; }}Copy the code

Creating a proxy object

public class Main {

    public static void main(String[] args) {
        SalaryCalculator calculator = new ManagerSalaryCalculator();
        InvocationHandler calculatorProxy = new SalaryLogProxy(subject);
        SalaryCalculator proxyInstance = (SalaryCalculator) Proxy.newProxyInstance(calculatorProxy.getClass().getClassLoader(), subject.getClass().getInterfaces(), calculatorProxy);
        proxyInstance.calculateSalary(1); }}Copy the code

Dynamic agent source code analysis

The dynamic proxy process is shown in the figure below. You can see that the dynamic proxy contains the following contents:

  • Target object: The object we need to proxy, corresponding to abovenew ManagerSalaryCalculator().
  • Interface: Methods that the target object and the proxy object need to provide together, as described aboveSalaryCalculator.
  • Proxy Proxy: Used to generate Proxy object classes.
  • Proxy object class: Proxy objects obtained by proxy and corresponding parameters.
  • Class loader: A class loader used to load proxy object classes, as described abovecalculatorProxy.getClass().getClassLoader().

Proxy.newProxyInstance

The key code for dynamic proxies is proxy.newProxyInstance (classLoader, interfaces, handler).

  • You can seeProxy.newProxyInstanceTwo things are done: 1. Get the constructor of the proxy object class, and 2. Instantiate the proxy object from the constructor.
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h) {
    Objects.requireNonNull(h);

    finalClass<? > caller = System.getSecurityManager() ==null
                                    ? null : Reflection.getCallerClass();

    /* * Look up or generate the designated proxy class and its constructor. */
    // Get the constructor of the proxy object class, which contains the proxy object class construction and loadingConstructor<? > cons = getProxyConstructor(caller, loader, interfaces);// Generate the proxy instance from the constructor.
    return newProxyInstance(caller, cons, h);
}
Copy the code

Proxy object class

Looking at the source code, we can see that the Proxy object classes extend the Proxy class and implement methods in the specified interface. Since Java cannot inherit more than one class, this class already inherits from Proxy and cannot inherit from other classes. So dynamic proxies in the JDK do not support proxies for implementation classes, only interfaces.

I am the god of the royal Fox. Welcome to follow my wechat official account

This article was first published to wechat public account, all rights reserved, reprint prohibited!