preface

Agent mode, also known as commission mode, is one of structural design modes. What is agent? In daily life, for example, ask a friend to pick up an express for you, ask a friend to do your homework for you, ask a friend to buy something for you, etc., the friend is your agent, you entrust your friend to do things, and there is also an agent in the world of code, and when you read more about design patterns, You will find a lot of design also has the shadow of the proxy mode, the mode of the proxy pattern is a very important design patterns, in Java, the proxy pattern is divided into static and dynamic agents, this article will through a simple example to explain the static agent, and then lead to the dynamic proxy, and further to explore the principle of dynamic proxy.

Definition of the proxy pattern

Provides a proxy for other objects to control access to this object.

Usage scenarios

Both static and dynamic proxies are applicable to the following scenarios:

1. When you do not want to access an object or it is difficult to access an object, you can create a proxy for the object and access the object indirectly through the proxy;

2. If the original object has different access permissions, the proxy can be used to control the access to the original object and protect the original object;

3. Perform some additional operations of your own while accessing the original object;

4. Provide local proxies for an object in different memory address Spaces, so that the system can hide the implementation of the server side and the client does not have to consider the existence of the server side, such as Binder in Android.

The following is a brief introduction to static proxies.

Static agent

1, the class diagram

Role Introduction:

  • Subject – an abstract Subject class that defines a common interface between the proxy object and the real object. It can be either an interface or an abstract class.
  • RealSubject – the RealSubject class, which can be called the delegated class or the proxied class, defines the real object represented by the proxy object and implements the Subject interface, while the Client indirectly calls the methods in the RealSubject class through the proxy class to perform the real business logic.
  • ProxySubject — a proxy class, also known as the delegate class or proxy class, which holds a reference to the real Subject class and also implements the Subject interface, and acts as a proxy by invoking the corresponding interface methods of the real Subject class in the interface methods of its implementation.
  • Client: The Client uses the proxy.

Structure of this class diagram is static agent, xiao Ming through intermediary rental example below reflect the effects of several characters, structure and the static and dynamic proxy class diagram agent is slightly different, will be in a dynamic agent there, but the whole idea and the static and dynamic proxy agent is the same, we also need to pay attention to the below mentioned some synonyms:

Subject = public interface;

ProxySubject = Proxy object = Proxy class = Proxy class = Proxy;

RealSubject = Real object = Proxy class = Delegate class = Principal;

I use different words for Subject, ProxySubject, and RealSubject depending on the context, as well as the verbs delegate and agent depending on the context.

2. Use static proxies

The basic steps for using a static proxy are:

1. Define the public interface between proxy objects and real objects;

2. Real objects implement the methods in the public interface;

3. Proxy objects implement methods in public interfaces and forward their logic to real objects.

We by xiao Ming to buy a house this example to explain the static agent, xiao Ming wanted to rent a house in big cities, but he is very busy no time to see at ordinary times, so he found a real estate agent, my rent will tell real estate agents, real estate agents to rent a house for himself to solve the problem, clearly real estate agent is the agent, xiao Ming is the man who is agent.

We implement this process using a static proxy, first defining a public interface for the rent step:

// Rent step public interface, the Subject role
public interface IRoom {
    void seekRoom(a);/ / to find housing
    void watchRoom(a);/ / the checking
    void room(a);// Give money to rent a house
    void finish(a);// Finish renting
}
Copy the code

Four steps to complete renting, very simple, and then we define the specific people who want to rent, namely Xiao Ming:

// The RealSubject is xiao Ming who wants to rent a house
public class XiaoMing implements IRoom {
    
    @Override
    public void seekRoom(a) {
        System.out.println("Looking for housing");
    }

    @Override
    public void watchRoom(a) {
        System.out.println("House");
    }

    @Override
    public void room(a) {
        System.out.println("Pay for rent.");
    }

    @Override
    public void finish(a) {
        System.out.println("Complete the rental."); }}Copy the code

This class implements the IRoom interface and its specific logic. However, Xiao Ming will not rent the house himself, he entrusts a real estate agent to do it, so a real estate agent is defined here:

// The agent, real estate agent, is the role of ProxySubject
public class RoomAgency implements IRoom {
    
    private IRoom mRoom;// Hold a reference to the principal (Xiaoming)
    
    public RoomAgency(IRoom room){
        this.mRoom = room;
    }
    
    @Override
    public void seekRoom(a) {
        mRoom.seekRoom();
    }

    @Override
    public void watchRoom(a) {
        mRoom.watchRoom();
    }

    @Override
    public void room(a) {
        mRoom.room();
    }

    @Override
    public void finish(a) { mRoom.finish(); }}Copy the code

In this class, we hold a reference to the agent. We can see that the real estate agent simply calls the methods in the agent. Let’s look at the execution relationship in the Client:

// The Client role
public class Client {
    public static void main(String[] args){
        // Xiao Ming wants to rent an apartment
        XiaoMing xiaoMing = new XiaoMing();
        // Find an agent, a real estate agent
        RoomAgency roomAgency = new RoomAgency(xiaoMing);
        // The real estate agent is looking for an apartment
		roomAgency.watchRoom();
        // The real estate agent looks at the house
        roomAgency.seekRoom();
        // Real estate agents rent houses
        roomAgency.room();
        // The real estate agent completes the rentalroomAgency.finish(); }}Copy the code

Output results:

Look for a house to rent money to complete the rentalCopy the code

The above is a silly process, a look to understand, real estate agent xiaoming’s house, house, rental process, you can see the static agent mode is very simple, is a kind of commission mechanism, real object method entrusted to the agent object, then real estate agent continue to act for others? Yes, for example, XiaoHong also wants to rent a house. Let’s define another XiaoHong implementation of IRoom interface and agent RoomAgency in the Client.

3 and disadvantages

But if Ming wants to buy a house rather than rent, can the real estate agent meet his needs? Obviously can’t, because the real estate agent only in the ability to rent it doesn’t have the ability to grind out to buy a house, then you need to replace interfaces for buying a house that rent a house, then define a specially to buy real estate agent, you will find that every time I change the interface, all need to change the proxy class, this is a static mode, only for a given interface implementation class do agent, If the interfaces are different, different proxy classes need to be defined. As the complexity of the system increases, it will be difficult to maintain the relationship between so many proxy classes and the proxy classes. At this time, dynamic proxy comes into being. Dynamic proxy can use one proxy class to proxy N proxy classes. When changing the interface, it does not need to redefine the proxy class. Because dynamic proxy does not need to define the proxy class in advance according to the interface, it delays the creation of the proxy class until the code runs.

4, and dynamic proxy difference

Let’s first review the loading of the class file that we wrote. A.class file is a binary file that only the JVM recognizes. Before the code runs, the JVM reads the.class file, parses the information in the.class file, retrieves the binary, loads it into memory. The corresponding Class object is generated.

The main difference between a static agent and a dynamic agent is that the.class file of a static agent already exists before our code is run. For example, roomagency. Java is compiled by Javac and becomes Roomagency. class. In contrast to static proxies, there is no.class file for the proxy class before the code is run, and the proxy class is generated dynamically when the code is running.

The following is to explain the dynamic agent, and through the dynamic agent to achieve again xiaoming house example.

A dynamic proxy

1, the class diagram

The Subject, ProxySubject, RealSubject, and Client roles are the same as those of a static proxy. InvocationHandler is an interface provided by Java. We need to define a class that implements the InvocationHandler interface. In this case, we call it the DynamicProxy role. Proxy is a class provided by Java for dynamically generating ProxySubject, which requires ProxySubject inheritance.

We see that DynamicProxy acts as a middleman before ProxySubject and RealSubject. ProxySubject will delegate things to DynamicProxy. DynamicProxy ends up delegating things to the RealSubject by saying: ProxySubject proxies DynamicProxy, and DynamicProxy proxies RealSubject. The most important point is that ProxySubject is generated dynamically when the code is running. This is the biggest difference between ProxySubject and static proxy.

Here’s a quick look at the InvocationHandler interface, the Proxy class.

1, InvocationHandler and Proxy function

To make it easier for us to implement dynamic proxies, Java provides the InvocationHandler interface and the Proxy class, both of which are in the java.lang.Reflect package. There is an inescapable relationship between dynamic proxies and reflection.

The InvocationHandler interface is defined as follows:

public interface InvocationHandler {
  /** * this method means that the proxy object calls the method * of the real object@paramProxy Proxy object *@paramMethod Specifies the method to be called on the real object@paramArgs The argument to the method being called */
  Object invoke(Object proxy, Method Method, Object[] args)throws Throwable;
}
Copy the code

The InvocationHandler interface executes the actual object’s method in the Invoke method. You can see that there is only one invoke method in the interface. We need to define a dynamic proxy class for the actual object that implements the invoke method in the interface. We also pass a reference to the real object in the method or construct, that is, the InvocationHandler implementation class needs to hold a reference to the real object in order to execute the methods of the real object.

The Proxy class is defined as follows:

public class Proxy implements Serializable {
    
    protected InvocationHandler h;// Holds a reference to type InvocationHandler

    protected Proxy(InvocationHandler h) {
        this.h = h;
    }

    // Get the Class object of the proxy object according to the specified Class loader and interface
    public staticClass<? > getProxyClass(ClassLoader loader, Class... interfaces)throws IllegalArgumentException {
        / /...
    }

    // Generate a proxy object based on the specified class loader and interface
    public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h) throws IllegalArgumentException {
        / /...
    }
    
    / /...
}

Copy the code

The Proxy class is used to dynamically create a Proxy object. It holds an InvocationHandler reference, which is passed in during construction. It provides a number of methods, but the common ones are getProxyClass and newProxyInstance:

  • GetProxyClass (emphasis method) : This method generates a proxy class binary stream at runtime based on the.class structure and loads the proxy class binary stream into a proxy class object via the incoming ClassLoader. The Proxy Class object inherits from Proxy and implements the list of interfaces for the second argument passed in.
  • NewProxyInstance (common method) : This method is used to generate an instance of the proxy Class at run time based on the proxy Class object. This method first calls getProxyClass to generate the proxy Class object. After obtaining the proxy Class object, The final result of newProxyInstance is to generate an instance of the Proxy object that inherits from the Proxy class and implements the given list of interfaces. It also holds an InvocationHandler reference internally.

The newProxyInstance method is often used to generate an instance of a proxy object.

3. Use dynamic proxies

The basic steps for using dynamic proxies are as follows:

1. Define the public interface between proxy objects and real objects; (Same as for static proxy)

2. Real objects implement the methods in the public interface; (Same as for static proxy)

3. Define a dynamic proxy class that implements the InvocationHandler interface.

Create a Proxy object using the newProxyInstance method of the Proxy class and call the methods of the Proxy object.

Step 1 and Step 2 are the same as the steps of static Proxy, so we will not repeat them. Compared with static Proxy, there is one less step: the method of implementing the public interface of the Proxy object. As mentioned earlier, the Proxy object is dynamically created by the Proxy when the code is running, so there is no need to write the class of the Proxy object in advance. Compared to a static proxy, there are two additional steps: 3, define a dynamic Proxy class that implements the InvocationHandler interface. 4, create a Proxy object using the newProxyInstance method of the Proxy class. Call the method of the Proxy object.

Step 3: We need to define a dynamic proxy class that executes the methods of real objects:

// A dynamic proxy class that implements the InvocationHandler interface
public class DynamicProxy implements InvocationHandler {

    private Object mObject;// A reference to a real object
    
    public DynamicProxy(Object object){
        this.mObject = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Invoke real object methods through reflection
        Object result = method.invoke(mObject, args);
        returnresult; }}Copy the code

In this class, we declare an Object reference to the real Object passed in the constructor, and invoke the actual method of the real Object through reflection. The thing to notice here is that it’s better to define the reference type to the real object as Objec rather than the actual object’s specific type like XiaoMing. The nice thing about this is that when you’re representing someone else, like xiaoHong, I just need to pass in the xiaoHong reference in the DynamicProxy constructor without changing the DynamicProxy class structure, so that one DynamicProxy can represent many people.

Next step 4: Create a Proxy object using the newProxyInstance method of the Proxy class, and invoke the method of the Proxy object. The following is the Client logic:

public class Client {
    public static void main(String[] args) {
        // Construct a xiaoming
        IRoom xiaoMing = new XiaoMing();
        // Construct a dynamic proxy
        InvocationHandler dynamicProxy = new DynamicProxy(xiaoMing);
        // Obtain the ClassLoader of the proxy class xiaoming
        ClassLoader classLoader = xiaoMing.getClass().getClassLoader();
        
        //1. Create a Proxy real estate agent dynamically using the newProxyInstance method of the Proxy class
        IRoom roomAgency = (IRoom) Proxy.newProxyInstance(classLoader, new Class[]{IRoom.class}, dynamicProxy);
        
        // Call the methods of the proxy object
        
        // The real estate agent is looking for an apartment
        roomAgency.watchRoom();
        // The real estate agent looks at the house
        roomAgency.seekRoom();
        // Real estate agents rent houses
        roomAgency.room();
        // The real estate agent completes the rentalroomAgency.finish(); }}Copy the code

The newProxyInstance method of the Proxy dynamically generates an instance of the Proxy object based on the class loader that is passed in. The generated Proxy object inherits from the Proxy class and implements the list of interfaces that are passed in. In this case, the ClassLoader is Xiaoming’s ClassLoader, which is the real object’s ClassLoader, and the interface list is IRoom. The IRoom Class object is passed in, and in addition to these two arguments, the dynamic proxy Class InvocationHandler instance is passed in. When the Proxy class creates an instance of the Proxy object, it will refer to this InvocationHandler. Then, when we call a method on the Proxy object, the logic of the method will be delegated to the Invoke method of the InvocationHandler instance. The invoke method calls our real object’s method through reflection.

Let’s look at the source code to see how the proxy object is generated and what the generated proxy object looks like.

4. Source code analysis

Let’s look at Client comment 1:

//1. Create a Proxy real estate agent dynamically using the newProxyInstance method of the Proxy class
IRoom roomAgency = (IRoom) Proxy.newProxyInstance(classLoader, new Class[]{IRoom.class}, dynamicProxy);
Copy the code

The Proxy’s newProxyInstance method will dynamically generate an instance of the Proxy object based on the class loader that is passed in.

//Proxy.java 
private static finalClass<? >[] constructorParams = { InvocationHandler.class };public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h) throws IllegalArgumentException{
    Objects.requireNonNull(h);

	// Clone the incoming interface list
    finalClass<? >[] intfs = interfaces.clone();//getProxyClass will forward the logic to getProxyClass0, so getProxyClass = getProxyClass0
    Call getProxyClass0 to get a proxy Class objectClass<? > cl = getProxyClass0(loader, intfs);try {
        
        //constructorParams = InvocationHandler.class
        // Constructor = Constructor; // Constructor = Constructor; // Constructor = Constructor
        finalConstructor<? > cons = cl.getConstructor(constructorParams);// The InvocationHandler reference passed in
        final InvocationHandler ih = h;
        // This Constructor is protected, so make it Public
        if(! Modifier.isPublic(cl.getModifiers())) { cons.setAccessible(true);
        }
        
        //3. Create an instance of a proxy object via Constructor reflection with the InvocationHandler argument and pass in the InvocationHandler reference to Constructor
        return cons.newInstance(new Object[]{h});
    } 
    / /... Omit exception handling
}
Copy the code

GetProxyClass0; getProxyClass0; getProxyClass0; getProxyClass0; getProxyClass0;

 public staticClass<? > getProxyClass(ClassLoader loader, Class<? >... interfaces)throws IllegalArgumentException{
        // getProxyClass0 is called in getProxyClass
        return getProxyClass0(loader, interfaces);
}
Copy the code

GetProxyClass0 = getProxyClass0 = getProxyClass0 = getProxyClass0 = getProxyClass0 = getProxyClass0 = getProxyClass0 At run time, a Proxy class binary stream is generated based on the structure of.class. The Proxy class binary stream is loaded into a Proxy class object via the passed ClassLoader. The Proxy class object inherits from Proxy and implements the list of interfaces passed in as the second parameter.

To see what this dynamically generated proxy Class object looks like, start with the following code at the beginning of the Client’s main function:

System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles"."true");
Copy the code

$proxy0.class is generated in the com/sun/proxy/ directory of your idea workspace. $proxy0.class is a dynamically generated proxy class object.

public final class $Proxy0 extends Proxy implements IRoom{
  private static Method m1;
  private static Method m3;
  private static Method m4;
  private static Method m2;
  private static Method m5;
  private static Method m6;
  private static Method m0;
  
  // Call the constructor of the parent Proxy, passing in the InvocationHandler reference
  public $Proxy0(InvocationHandler paramInvocationHandler){
      super(paramInvocationHandler);
  }
  
  / / the following four methods are implemented since IRoom method, you can see they are just simple call the parent class h invoke method, and the object of agency $Proxy0 instance, to invoke method, and parameters inside
  public final void watchRoom(a){
    try{
      this.h.invoke(this, m3, null);
      return;
    }
    / /... Omit exception handling
  }
  
  public final void room(a){
    try{
      this.h.invoke(this, m4, null);
      return;
    }
    / /... Omit exception handling
  }
  
  public final void seekRoom(a){
    try{
      this.h.invoke(this, m5, null);
      return;
    }
    / /... Omit exception handling
  }
    
      
  public final void finish(a){
    try{
      this.h.invoke(this, m6, null);
      return;
    }
    / /... Omit exception handling
  }
  
 / /... We only focus on the IRoom interface methods, so I omit the toSting, hashcode, and other inherited methods from Object. The logic is the same, they all call the h invoke method of the parent class

  static{
    try{
      m0 = Class.forName("java.lang.Object").getMethod("hashCode".new Class[0]);
      m1 = Class.forName("java.lang.Object").getMethod("equals".new Class[] { Class.forName("java.lang.Object")}); m2 = Class.forName("java.lang.Object").getMethod("toString".new Class[0]);
      // Get the Method object of the IRoom interface Method
      m3 = Class.forName("com.example.hy.designpatternDemo.proxy.IRoom").getMethod("watchRoom".new Class[0]);
      m4 = Class.forName("com.example.hy.designpatternDemo.proxy.IRoom").getMethod("room".new Class[0]);
      m5 = Class.forName("com.example.hy.designpatternDemo.proxy.IRoom").getMethod("seekRoom".new Class[0]);
      m6 = Class.forName("com.example.hy.designpatternDemo.proxy.IRoom").getMethod("finish".new Class[0]);
      return;
    }
    / /... Omit exception handling}}Copy the code

I’ve omitted the irrelevant code for the sake of reading, but you can see that the Proxy class getProxyClass0 method will dynamically generate the Proxy object $proxy0.class, which will inherit from the Proxy class and the list of implementation interfaces, and the only interface passed in here is IRoom, So $Proxy0 will implement IRoom methods, and the logic in these methods is to invoke the invoke method of the parent class h, which is the InvocationHandler reference. If we go back to the newProxyInstance method comments 2 and 3, You’ll notice that the InvocationHandler reference was passed in the construct when the $Proxy0 instance was created through reflection.

$Proxy0 is a static block at the bottom of $Proxy0. It is a static block at the bottom of $Proxy0. It is a static block at the bottom of $Proxy0. $Proxy0 = $Proxy0; $Proxy0 = $Proxy0; $Proxy0 = $Proxy0;

// A dynamic proxy class that implements the InvocationHandler interface
public class DynamicProxy implements InvocationHandler {

    private Object mObject;// A reference to a real object
    
    public DynamicProxy(Object object){
        this.mObject = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Invoke real object methods through reflection
        Object result = method.invoke(mObject, args);
        returnresult; }}Copy the code

So when we call the newProxyInstance method of the Proxy class, which creates the Proxy object in it, and returns an instance of the Proxy object $Proxy0, so when we call the method of the Proxy object, we’re calling the corresponding method of $Proxy0. This method processing logic is then delegated to the Invoke method of the InvocationHandler instance (the parent of the proxy object holds the InvocationHandler reference). The Invoke method calls the method of our real object through reflection (the InvocationHandler implementation class holds the reference to the real object), and this is the whole dynamic proxy process.

Principle 5,

This is because the JVM can load a class object using binary information from a.class file. So, if the code is running, Follow. The class file format and structure, generate the corresponding binary data, and then the binary data by JVM loading into the corresponding class object, have a class object, we can at runtime by reflection to create a proxy object instance, when this completes the code to run, the ability to dynamically create a proxy object, This is how dynamic proxies work.

conclusion

In static proxy mode, the methods in the proxy class ProxySubject are specified to call the methods corresponding to a particular ReadSubject; In the dynamic proxy mode, each invocation of a method in the ProxySubject class is handled by the InvocationHandler, and the InvocationHandler calls the methods of the RealSubject. Here is a table to summarize this article:

advantages disadvantages The difference between
Static agent 1. The proxy class acts as the intermediary between the client and the proxy class, and plays the role of protecting the proxy class

2. Decoupling the proxy class and the proxy class through the interface reduces the coupling degree of the system
1, can only be a proxy for the implementation class under the given interface, if the interface is different, then we have to redefine different proxy classes, maintenance complex

2. Request processing is slow due to the addition of proxy objects between the client and the proxy class
You need to implement the interface to write the proxy class ahead of time. Before the code runs, the.class file for the proxy class will already exist
A dynamic proxy 1, the proxy class is automatically generated by reflection when the program is running, we do not need to manually write proxy class code, simplify the programming work

2, a dynamic proxy class InvocationHandler can represent multiple proxy classes, more flexible
1. Dynamic proxies can only proxy classes that implement interfaces, not classes that implement abstract classes

2. It is inefficient to invoke the methods of the proxy class through reflection
There is no need to implement the interface in advance to write the proxy class. The JVM creates the proxy class dynamically while the code is running

Agent mode is widely used and should be selected according to the actual situation in the actual development.

Source code of this article

References:

This is a comprehensive & clear study guide to dynamic Proxy patterns