Java Dynamic agent and static agent and what it can do for us

Keywords: Java less Programming REST

I believe that we have more or less come into contact with Java's agent mode on the Internet and in our daily study and work. We often hear some terms about static agent and dynamic agent. But do we really know that? At least when I interviewed, I found that many people were not very clear.

First of all, agents are easy to understand, that is, helping a person or a group of people to do something. When migrating to object-oriented programming, agent is to help a class do something, and the tool of agent is called agent class.

What's the advantage of acting? This is just like what factories and distributors do. Factories can sell their own products directly, and distributors can also sell the products produced by factories. So why are there distributors? Because the distributor can provide some additional services, or can complete some other things in the process of sales, such as combination sales, doing activities according to local conditions, etc., which may not be concerned or controlled by the factory. Such function and role contracting to agents will make the division of labor more clear, and can provide some additional or customized services.

 

Static agent

The proxy methods in Java can be divided into static proxy and dynamic proxy. The meaning of static agent is that the agent class / object has been determined or existed before the program we are concerned about runs. Static agent is easy to understand. We often use it in our daily work, such as an existing interface. We don't expect to change it, but now we need to add some new logic or functions to the original logic, such as sending a message after the original interface method call. So we can create a class to implement the original interface, and inject the existing interface as a member variable, call the methods in it, and add the functions we need.

The class diagram of static proxy is shown below. The implementation class and proxy class that need to be proxy implement abstract interface AbstractInterface, while the relationship between InterfaceProxy and InterfaceImpl is aggregation.

  

 

 

 

Take a look at a sample code. ProductAuditCallbackService is an existing interface. For some reasons, this interface cannot be used externally. We need to define a new interface with the same name (mainly for the convenience of customer understanding and corresponding to the original interface), but we need to add a little "new logic". Therefore, we can also implement ProductAuditCallbackService. ProductAuditCallbackServiceProxy is our proxy class. After that, external calls can instantiate our proxy class and call methods with the same name.

 

 1 public class ProductAuditCallbackServiceProxy implements ProductAuditCallbackService {
 2 
 3     @Resource
 4     private ProductAuditCallbackService productAuditCallbackService;
 5 
 6     @Override
 7     public Result<Void> auditProduct(ProductAuditRequest request, String auditStatus) {
 8         if (auditStatus == "DELETED") {
 9             return new Result<>();
10         }
11         return productAuditCallbackService.auditProduct(request, auditStatus);
12     }
13 
14 
15 ...
16 }

 

 

 

 

Dynamic agent

The main difference between dynamic agents and static agents is that they need to generate agent classes at run time. When using dynamic proxy, we also need to define a mediation class between the proxy class and the delegate class, and the mediation class needs to implement java.lang.reflect.InvocationHandler interface.

 1 package java.lang.reflect;
 2 
 3 /**
 4  * {@code InvocationHandler} is the interface implemented by
 5  * the <i>invocation handler</i> of a proxy instance.
 6  *
 7  * <p>Each proxy instance has an associated invocation handler.
 8  * When a method is invoked on a proxy instance, the method
 9  * invocation is encoded and dispatched to the {@code invoke}
10  * method of its invocation handler.
11  *
12  * @author      Peter Jones
13  * @see         Proxy
14  * @since       1.3
15  */
16 public interface InvocationHandler {
17 
18     public Object invoke(Object proxy, Method method, Object[] args)
19         throws Throwable;
20 }

  

Dynamic proxies are used frequently in framework class code, and can make our code look more advanced, so why not? Let's look at some practical examples.

MethodInvocationHandler is a mediation class, which implements the InvocationHandler interface. The function of MethodMonitor is to count the number and time-consuming of methods called in the business object of our delegation class. Because its main function is not the main content we are concerned about, its implementation is ignored.

 1 public class MethodInvocationHandler implements InvocationHandler {
 2 
 3     //Represented object
 4     private Object business;
 5 
 6     private final MethodMonitor methodMonitor;
 7 
 8     public MethodInvocationHandler(MethodMonitor methodMonitor) {
 9         this.methodMonitor = methodMonitor;
10     }
11 
12     /**
13      * Agent method
14      */
15     @Override
16     public Object invoke(Object proxy, Method method, Object[] args)
17             throws Throwable {
18 
19         long startTime = System.currentTimeMillis();
20 
21         Object result = method.invoke(this.business, args);
22 
23         //Method call statistics
24         this.methodMonitor.methodCount(this.business.getClass().getSimpleName() + POINT + method.getName(), startTime);
25         return result;
26     }
27 
28 }

The rest of the sample code and external call examples are as follows. Our Business class has three methods. MethodSampleClient is an encapsulated client. We don't want external clients to perceive our implementation and the relationship with Business, so we define a member variable proxy in MethodSampleClient. When external needs some functions provided by Business, we provide it through proxy. Proxy.newProxyInstance() is the way we instantiate a proxy class. Yo, this is also a factory mode. You can read some descriptions of this method. The three parameters that need to be passed in are: ClassLoader of the class to be proxy, collection of interfaces to be proxy for the class to be proxy, and instance of the mediation processing class.

What I'm writing here is a certain class. In fact, in the actual development work, we often define abstract interfaces or abstract classes. Only when we know which instance of the implementation class is determined by the runtime, it may be easier to understand: the runtime determines the implementation class of the delegate class, the runtime generates the proxy class, and calls the corresponding methods of the delegate class.

 

 1 public class Business {
 2 
 3     public void createJob() {
 4         System.out.println("test createJob");
 5     }
 6 
 7 
 8     public void processJob() {
 9         System.out.println("test processJob");
10     }
11 
12     public void closeJob() {
13         System.out.println("test closeJob");
14     }
15 
16 }
17 
18 
19 
20 public class MethodSampleClient {
21 
22     private Business business;
23 
24     @Getter
25     private Object proxy;
26 
27     private InvocationHandler invocationHandler;
28 
29 
30     public void init() {
31         this.business = new Business();
32         this.invocationHandler = new MethodInvocationHandler(new MethodMonitor());
33         this.proxy = bind(this.business, invocationHandler);
34     }
35 
36     /**
37      * Bind object, initialize directly and return proxy class for client use
38      */
39     public Object bind(Object business, InvocationHandler invocationHandler) {
40         return Proxy.newProxyInstance(
41                 //Of the proxied class ClassLoader
42                 business.getClass().getClassLoader(),
43                 //Interface to be proxied,The return object of this method will automatically claim to implement these interfaces
44                 business.getClass().getInterfaces(),
45                 //Agent object
46                 invocationHandler);
47     }
48     
49 }    
50 
51 
52 /**
53 *  A simple client test class
54 */
55 public class Test {
56 
57     public void main(String[] args) {
58         MethodSampleClient methodSampleClient = new MethodSampleClient();
59         methodSampleClient.init();
60 
61         methodSampleClient.getProxy().createJob();
62         methodSampleClient.getProxy().processJob();
63         methodSampleClient.getProxy().closeJob();
64     }
65 
66 }

   

In order to make this process clear, I actually wrote a lot of code, which seems more complicated. To summarize, dynamic agents simply follow the steps below to write code:

  • First of all, the delegation class to be represented should be specified.
  • Implement the InvocationHandler interface and define a mediation class.
  • With Proxy.newProxyInstance() instantiate the proxy class and use it directly in the client code.

Well, it's about the same. The most important thing is to be able to consciously use and experience its role in practical work - software development is driven by experience rather than knowledge.

Posted by -Karl- on Sun, 31 May 2020 03:05:13 -0700