no_mkd
I. What is the Proxy Pattern?
As the name suggests, a proxy is a third party, like a celebrity's agent. The celebrity hands over all their affairs to the agent to handle. The celebrity just tells the agent what to do, and the agent will figure out how to do it, then report the result back to the celebrity.
Originally, there was direct interaction between the caller and the callee. Now, the caller and callee are separated, and the proxy is responsible for transmitting information to complete the call.
II. What is the Use of the Proxy Pattern?
The Proxy Pattern is a large pattern, so it has wide applications, as can be seen from the types of proxies:
Remote Proxy: One of the most classic proxy patterns. The remote proxy is responsible for communicating with a remote JVM to achieve normal interaction between a local caller and a remote callee.
Virtual Proxy: Used to replace huge objects, ensuring they are only created when needed.
Protection Proxy: Provides access control for the callee, verifying the caller's permissions.
In addition, there are firewall proxies, smart reference proxies, cache proxies, synchronization proxies, complex hiding proxies, copy-on-write proxies, etc., each with their own special purposes.
P.S. Remote Proxy is the most basic proxy pattern, and it's necessary to discuss it separately, so this article explains it in detail. Other proxies will be described in detail in a supplementary blog post.
III. Remote Proxy
Some things can be easily solved without a proxy, but some things must rely on a proxy to complete. For example, to call a method on another machine, we may have to use a proxy.
The internal mechanism of remote proxy is as follows:

Let me explain: Stub represents the Server object.
Skeleton represents the Client (I don't know why they're called "Stub" and "Skeleton", but of course, there's no need to know).
Why is Stub the proxy for the service instead of the client, even though it's on the client side? Because the client needs to interact with the server, but now the service is in a remote JVM and cannot interact directly, so Stub is used to represent the Server. Calling Stub is equivalent to calling Server (the internal communication mechanism is transparent to the Client. For the Client, there's no difference between calling Stub and directly calling Server, and this is one of the advantages of the Proxy Pattern).
The specific process is as follows:
- Client sends a method call request to Stub (Client thinks Stub is Server)
- Stub receives the request and communicates with the server-side Skeleton through Socket, passing the call request to Skeleton
- Skeleton receives the request and calls the local Server (sounds a bit strange, here Server is equivalent to Service)
- Server performs the corresponding action and returns the result to the caller Skeleton
- Skeleton receives the result and sends it to Stub through Socket
- Stub passes the result to Client
P.S. Both step 2 and step 5 require Socket communication. Everything passed between them must be serialized before sending and deserialized after receiving. This explains why public method return values in Server must be serializable.
IV. Implementation of Remote Proxy
There are two ways to implement remote proxy:
- Customize Stub and Skeleton (implement their internal communication)
- Use Java's supported RMI (Remote Method Invocation) to implement, which can save a lot of trouble, but it's not easy to understand the internal principles
First, here's an example of the custom approach. There's a great blog post about this, so I've taken the liberty to record the link: Click me to jump>>
The original article provides a complete example, so I won't repeat it here. I'll add a pseudo-class diagram to the original article for easier understanding:

(Don't ask me why the class diagram is drawn this way; I said it's a "pseudo" class diagram...)
If you read the original article carefully, remote proxy is not hard to understand. Stub and Skeleton are responsible for communication, similar to a chat program written with Socket. There's nothing special beyond that.
Below is the implementation of the proxy pattern using Java's supported RMI, where you can clearly feel that many details are hidden.
First, we need to define a remote interface:
package ProxyPattern;import java.rmi.RemoteException;
/**
-
Define service interface (extends java.rmi.Remote interface)
-
@author ayqy / public interface Service extends java.rmi.Remote{ / 1. Method return type must be serializable Serializable
- 2. Every method must declare exception throws RemoteException (because it's RMI mode)
- */
/**
- @return Complete greeting sentence
- @throws RemoteException */ public String greet(String name) throws RemoteException; }
Note: The return type of public methods in the service interface must be serializable (in other words, custom return types must implement the Serializable interface), and the String type has already implemented the Serializable interface.
Why define such an interface extending java.rmi.Remote? The API documentation provides a clear explanation:
The Remote interface serves to identify interfaces whose methods may be invoked from a non-local virtual machine. Any object that is a remote object must directly or indirectly implement this interface. Only those methods specified in a "remote interface", an interface that extends java.rmi.Remote are available remotely.
Simply put, it's to tell the compiler: our Service object can be called remotely, that's all.
After defining the remote interface, of course, we also need a concrete implementation:
package ProxyPattern;import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject;
/**
-
Implement remote service (extends UnicastRemoteObject and implements custom remote interface)
-
@author ayqy */ public class MyService extends UnicastRemoteObject implements Service{
/**
- Used to verify program version (receiver will verify UID during deserialization, exception if mismatched) */ private static final long serialVersionUID = 1L;
/**
- Empty constructor method, just for declaring exceptions (default constructor method won't declare exceptions)
- @throws RemoteException */ protected MyService() throws RemoteException { }
@Override public String greet(String name) throws RemoteException { return "Hey, " + name; } }
P.S. The service inherits the UnicastRemoteObject class to automatically generate the Stub class (UnicastRemoteObject encapsulates the specific generation details, saving us the work of one class).
Having the service on the server side is not enough. We need a Server to help us start the RMI registration service and register the remote object for client calls:
package ProxyPattern;import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry;
/**
- Implement server class, responsible for starting service and registering service object
- @author ayqy */ public class Server { public static void main(String[] args){ try { //Start RMI registration service, specify port as 1099 (1099 is the default port) LocateRegistry.createRegistry(1099); //Create service object MyService service = new MyService(); //Register service to RMI registration server, named MyService Naming.rebind("MyService", service); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Note that in addition to using LocateRegistry.createRegistry() to start the service, you can also use the command line method (rmiregistry command), with the same effect.
The server-side code is complete here. Like a Socket chat program, we need to write two parts of code: Server and Client. Now let's implement the client side, very simple, like a test class:
package ProxyPattern;/* Reference materials:
-
- How to use JAVA RMI
- http://blog.csdn.net/afterrain/article/details/1819659
-
- RMI internal principles
- http://www.cnblogs.com/yin-jingyu/archive/2012/06/14/2549361.html
- */ import java.rmi.Naming;
/**
- Implement client class
- @author ayqy
/
public class Client {
/*
-
Find remote object and call remote method */ public static void main(String[] argv) { try { //If you want to find the MyService object from another machine that has started the RMI registration service, just modify the IP address Service service = (Service) Naming.lookup("//127.0.0.1:1099/MyService");
//Call remote method System.out.println(service.greet("SmileStone"));} catch (Exception e) { System.out.println("Client exception: " + e); } } }
-
P.S. We directly use Naming.lookup() to get the Stub object (yes, it's Stub; the real object is still on another machine, of course we can't get it. What we get here is just the Stub proxy of Service), then call the proxy's method to get the result.
Note this detail:
//If you want to find the MyService object from another machine that has started the RMI registration service, just modify the IP address
Service service = (Service) Naming.lookup("//127.0.0.1:1099/MyService");
Although it's just one line, it hides two details:
- The client must know the service interface Service. Here, since it's in the same package locally, we don't need to worry about it. In real applications, Client and Server are separated, so the Client needs to get a copy of the Service interface, otherwise it cannot be called.
- The client must know the server's IP and port number (for communication, you can't do without this).
After working hard for so long, let's see the running result (run Server first, then run Client):

P.S. For the part about implementing remote proxy using Java's supported RMI, the reference material is another person's blog post, which explains it in more detail.
P.S. As for starting the RMI registration service via command line, it's too troublesome, and you need to generate the Stub class first. This method is not recommended. Specific operation details are also introduced in detail in the blog post link above, so I won't repeat them here.
Note: Until we see the test results with our own eyes, we have never customized Stub and Skeleton classes, right? Yes, that's correct. They do exist, but they are hidden by RMI (it is said that after Java 1.5, there is no Skeleton in RMI, and even in higher versions, there is no Stub... but that doesn't matter; we have already understood the original remote proxy).
V. Summary
Looking back, think about what remote proxy does:
- Intercept and control method calls (this is also the biggest feature of the proxy pattern, the most typical being firewall proxy...)
- The existence of remote objects is transparent to the client (the client completely treats the Stub proxy object as the remote object, although the client is a bit curious about why exceptions may occur...)
- Remote proxy hides communication details
When we need to call a method of a specified object on another machine (JVM), using remote proxy is a good choice...
No comments yet. Be the first to share your thoughts.