An overview,

1. What is agency

We all know that wechat business agent, simply speaking is to replace the manufacturer to sell goods, manufacturers “commission” agent for its sales of goods. About wechat business agent, first of all, when we buy things from them, we usually do not know who is behind the manufacturer, that is to say, “consignor” is invisible to us; Secondly, wechat business agent mainly to the circle of friends as the target customers, which is equivalent to the manufacturer to do a “filter” on the customer group. We further abstract the wechat business agent and the manufacturer, the former can be abstracted into the agent class, the latter can be abstracted into the delegate class (agent class). Through the use of agents, there are usually two advantages, and can be respectively corresponding to the two characteristics of wechat business agents we mentioned:

Advantage 1: you can hide the implementation of the delegate class;

Advantage two: Decoupling the client from the delegate class, allowing you to do some extra processing without modifying the delegate code.

2. Static proxy

If the proxy class exists before the program runs, it is called a static proxy. In this case, the proxy class is usually defined in Java code. Typically, the proxy and delegate classes in a static proxy implement the same interface or derive from the same parent class. Vendor class represents the manufacturer and BusinessAgent class represents the wechat BusinessAgent to introduce the simple implementation of static agent. Both the delegate class and the agent class realize the Sell interface. The definition of the Sell interface is as follows:

/** * public interface Sell {void Sell (); void ad(); }Copy the code

The Vendor class is defined as follows:

/** * public class Vendor implements Sell {public voidsell() { 
        System.out.println("In sell method"); 
    } 
    
    public void ad() { 
        System,out.println("ad method"); }}Copy the code

The BusinessAgent class is defined as follows:

/** * public class implements Sell {private Sell vendor; public BusinessAgent(Sell vendor){ this.vendor = vendor; } public voidsell() { 
        vendor.sell();
    } 
    
    public void ad() { vendor.ad(); }}Copy the code

As you can see from the definition of the BusinessAgent class, static proxies can be implemented through aggregation, with the proxy class holding a reference to the delegate class.

Let’s consider this requirement: add a filter to the Vendor class to only sell to college students. With static agents, we don’t need to modify the code of the Vendor class to do this, just add a judgment to the sell method in the BusinessAgent class as follows:

/** * proxy class */ public classBusinessAgent(){ implements Sell {
    private Sell vendor;
    
    public BusinessAgent(Sell vendor){
        this.vendor = vendor;
    }

    public void sell() {
        if (isCollegeStudent()) {
            vendor.sell();
        }
    } 
    
    public void ad() { vendor.ad(); }}Copy the code

This corresponds to the second advantage of using a proxy we mentioned above: decoupling the client from the delegate class, allowing you to do some extra processing without modifying the delegate class code. The limitation of static proxy is that the proxy class must be written before running. Here we focus on the dynamic proxy way of generating proxy class at run time.

Dynamic proxy

1. What is dynamic proxy

The way proxy classes create proxies while the program is running is called dynamic proxies. That is, in this case, the proxy class is not defined in Java code, but is dynamically generated at run time according to our “instructions” in Java code. The advantage of dynamic proxy over static proxy is that it is easy to uniformly handle the functions of the proxy class without modifying the functions of each proxy class. Let’s use an example to illustrate the advantages of dynamic proxies.

Now, suppose we want to implement a requirement that prints “before” before executing a method in a delegate class and “after” after. We will introduce the Vendor class as the delegate class and the BusinessAgent class as the agent class in the above example. First let’s use static proxy to implement this requirement. The code is as follows:

public class BusinessAgent implements Sell {
    private Vendor mVendor; 
 
    public BusinessAgent(Vendor vendor) {
        this.mVendor = vendor; 
    } 
 
    public void sell() {
        System.out.println("before"); 
        mVendor.sell(); 
        System.out.println("after"); 
    } 
 
    public void ad() {
        System.out.println("before"); 
        mVendor.ad(); 
        System.out.println("after"); }}Copy the code

As we can see from the code above, implementing our requirements through static proxies requires us to add logic to each method. There are only two methods, so it is not too much work. What if the Sell interface contains hundreds of methods? There is a lot of redundant code to write with static proxies. By using dynamic proxies, we can make a “unified directive” so that methods of all proxy classes are treated uniformly, rather than changing each method individually. Let’s take a look at how to implement our requirements using dynamic proxies.

2. Use dynamic proxies

2.1 InvocationHandler interface

When using dynamic proxies, we need to define a mediation class that sits between the proxy class and the delegate class. This mediation class is required to implement the InvocationHandler interface, which is defined as follows:

Public interface InvocationHandler {Object invoke(Object proxy, Method Method, Object[] args); }Copy the code

As we know from the name InvocationHandler, the mediation class that implements this interface is used as the “call handler.” When we call a method on a proxy object, this “call” is passed to the Invoke method. The proxy object is passed in as a proxy parameter. The method parameter identifies which method of the proxy class we are calling, and args is the method parameter. In this way, all of our calls to methods in the proxy class become calls to Invoke, which allows us to add uniform processing logic to the Invoke method (or to do different processing for different proxy class methods based on the Method argument). So we simply print “before” in the invoke method implementation of the mediation class, then call the invoke method of the delegate class, and then print “after”. Let’s implement it step by step.

2.2 Definition of delegate class

In dynamic proxy mode, the delegate class is required to implement a certain interface. Here we implement the Sell interface. The Vendor class is defined as follows:

public class Vendor implements Sell { 
    public void sell() { 
        System.out.println("In sell method"); 
    }

    public void ad() {
        System,out.println("ad method"); }}Copy the code

2.3 the mediation class

As mentioned above, the mediation class must implement the InvocationHandler interface to act as the calling handler to “intercept” calls to the proxy class methods. A mediation class is defined as follows:

Public class DynamicProxy implements InvocationHandler {//obj implements InvocationHandler; private Object obj; public DynamicProxy(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before"); 
        Object result = method.invoke(obj, args); 
        System.out.println("after"); 
        returnresult; }}Copy the code

As you can see from the above code, the mediation class holds a reference to a delegate object and invokes the corresponding method of the delegate object in the Invoke method. Does this sound familiar?

A reference to a delegate object is held in an aggregate manner, and external calls to Invoke are eventually converted to calls to a delegate object. Isn’t this an implementation of the static proxy we introduced above?

In fact, the mediation class and the delegate class constitute a static proxy relationship. In this relationship, the mediation class is the proxy class, and the delegate class is the delegate class.

The proxy and mediation classes also form a static proxy relationship, in which the mediation class is the delegate class and the proxy class is the proxy class.

In other words, dynamic proxy relationships consist of two sets of static proxy relationships, which is the principle of dynamic proxy. Let’s take a look at how to “indicate” to dynamically generate proxy classes.

2.4 Dynamically Generating proxy Classes

The code for dynamically generating proxy classes is as follows:

Public class Main {public static void Main (String[] args) {DynamicProxy inter = new DynamicProxy(new) Vendor()); // Add this sentence to produce one$Proxy0.class file, which is the dynamically generated proxy class file system.getProperties ().put("sun.misc.ProxyGenerator.saveGeneratedFiles"."true"); / / get the Proxy class instance sell sell sell = (sell) (Proxy. NewProxyInstance (sell. Class. GetClassLoader (), new class [] {sell. Class}, Intel)); // Invoking a proxy class method from a proxy class object actually goes to invoke and calls sell-.sell (); sell.ad(); }}Copy the code

In the above code, we call the newProxyInstance method of the Proxy class to get an instance of the Proxy class. This proxy class implements the interface we specify and distributes method calls to the specified invocation handler. This method is declared as follows:

public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h) throws IllegalArgumentExceptionCopy the code

The meanings of the three parameters of the method are as follows:

Loader: a ClassLoder that defines the proxy class; H: The calling handler, which is the instance of the class we defined above that implements the InvocationHandler interface

Let’s run it and see if our dynamic proxy works. The output of my run here is zero

It shows that our dynamic proxy really works.

We have briefly mentioned the principle of dynamic proxy, here is a brief summary: The newProxyInstance method is first used to obtain the instance of the proxy class, and then we can call the method of the proxy class through this instance. All calls to the method of the proxy class will actually call the invoke method of the mediation class (calling handler), from which we call the corresponding method of the delegate class. And you can add your own processing logic.

3. Agency mode

This is probably the simplest of the design patterns, class diagrams

The biggest characteristic of the proxy mode is that the proxy class and the actual business class implement the same interface (or inherit the same parent class), the proxy object holds a reference to the actual object, the external call is the operation of the proxy object, and in the internal implementation of the proxy object will call the operation of the actual object

Java dynamic proxy is also internally implemented through Java reflection mechanism, that is, an object is known, and then dynamically calls its methods at run time, so that some corresponding processing before and after the call

Let’s say we have an application that requires logging before and after a call to a method of a class,

A common interface

public interface AppService {  
    public boolean createApp(String name);  
}  
Copy the code

The default implementation class for this interface

public class AppServiceImpl implements AppService {  
    public boolean createApp(String name) {  
        System.out.println("App["+name+"] has been created.");  
        return true; }}Copy the code

Log handlers (essentially acting as mediation classes)

*/ Public class LoggerInterceptor implements InvocationHandler {private Object target; Public LoggerInterceptor(Object target){this.target = target; } public Object invoke(Object proxy, Method method, Object[] arg) throws Throwable { System.out.println("Entered "+target.getClass().getName()+"-"+method.getName()+",with arguments{"+arg[0]+"}"); Object result = method.invoke(target, arg); // Call the target object's method system.out.println ("Before return:"+result);  
        returnresult; }}Copy the code

Outside calls

public class Main { public static void main(String[] args) { AppService target = new AppServiceImpl(); AppService proxy = (AppService) proxy.newProxyInstance (target.getClass().getClassLoader(), target.getClass().getInterfaces(), new LoggerInterceptor(target)); proxy.createApp("Kevin Test"); }}Copy the code