Agent model of structural model (static agent, dynamic agent [principle], CGLIB agent)

Keywords: Java Design Pattern

5. Structural mode

Structural pattern describes how to form a larger structure of columns or objects according to a certain layout. It is divided into class structure pattern and object structure pattern. The former uses inheritance mechanism to organize interfaces and classes, and the latter uses composition or aggregation to combine objects.

Because the coupling degree of composite relationship or aggregation relationship is lower than that of inheritance relationship and meets the "composite Reuse Principle", all object structural patterns have greater flexibility than class structural patterns.

The structural mode is divided into the following 7 types

  • proxy pattern
  • Adapter mode
  • Decorator mode
  • Bridging mode
  • Combination mode
  • Sharing element mode

5.1 agent mode

5.1.1 general

Because some circle centers need to provide a proxy for an object to control access to the object, at this time, the access object is not suitable or can not directly reference the target object. The proxy object acts as an intermediary between the access object and the target object.

Agents in Java are divided into static agents and dynamic agents according to different generation times of agent classes. Static agent classes are generated during compilation, while dynamic agents are generated dynamically during Java runtime. Dynamic agents include JDK agents and CGLIB agents

5.1.2 structure

The Proxy mode is divided into three roles:

  • Abstract Subject class: business methods implemented by declaring real subjects and proxy objects through interfaces or abstract classes.
  • Real Subject class: it implements the specific business in the abstract subject. It is the real object represented by the proxy object and the object to be referenced finally.
  • Proxy class: it provides the same interface as the real topic. It contains references to the real topic. It can access, control or extend the functions of the real topic

5.1.3 static proxy

Train station ticket

If you want to buy a train ticket, you need to go to the railway station to buy a ticket, take a bus to the railway station, queue up and a series of operations, which is obviously very troublesome. The railway station has consignment points in many places, so it is much more convenient for us to buy a ticket at the consignment point. This example is a typical agency model. The railway station is the target object, and the consignment point is the agent object. The class diagram is as follows:

The code is as follows

//Ticket selling interface
public interface SellTickets {
    void sell();
}

//The railway station has the function of selling tickets, so the SellTickets interface needs to be implemented
public class TrainStation implements SellTickets {

    public void sell() {
        System.out.println("Train station ticket");
    }
}

//Consignment point
public class ProxyPoint implements SellTickets {

    private TrainStation station = new TrainStation();

    public void sell() {
        System.out.println("The agency charges some service fees");
        station.sell();
    }
}

//Test class
public class Client {
    public static void main(String[] args) {
        ProxyPoint pp = new ProxyPoint();
        pp.sell();
    }
}

From the above code, we can see that the test class accesses the ProxyPoint class object, that is, the ProxyPoint composition accesses the mediation between the object and the target object, and modifies the Sell method at the same time
Enhancement (agents charge some service fees)

5.1.4 JDK dynamic agent

Next, we use the dynamic proxy to implement the above case. First, we talk about the dynamic proxy provided by JDK. Java provides the dynamic proxy class object proxy. Proxy is not the class of the proxy object, but provides a static method (newProxyInstance method) to create a dynamic proxy to obtain the proxy object

The code is as follows

Interface

//Ticket selling interface
public interface SellTickets {
    void sell();
}

train station

//The railway station has the function of selling tickets, so the SellTickets interface needs to be implemented
public class TrainStation implements SellTickets {

    public void sell() {
        System.out.println("Train station ticket");
    }
}

Agent factory

//Proxy factory, used to create proxy objects
public class ProxyFactory {

    private TrainStation station = new TrainStation();

    public SellTickets getProxyObject() {
        //Get Proxy object using Proxy
        /*
            newProxyInstance()Method parameter description:
                ClassLoader loader :  Class loader, which is used to load proxy classes. You can use the class loader of real objects
                Class<?>[] interfaces :  The interface implemented by the real object, the proxy mode, and the real object and the proxy object implement the same interface
                InvocationHandler h :  Call handler for proxy object
         */
        SellTickets sellTickets = (SellTickets) 			      			Proxy.newProxyInstance(station.getClass().getClassLoader(),
                station.getClass().getInterfaces(),
                new InvocationHandler() {
                    /*
                        InvocationHandler Parameter description of invoke method in:
                            proxy :  Proxy object
                            method :  Method instance corresponding to the interface method called on the proxy object
                            args :  The actual parameters passed when a proxy object calls an interface method
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        System.out.println("The agency charges some service fees(JDK Dynamic agent mode)");
                        //Execute real objects
                        Object result = method.invoke(station, args);
                        return result;
                    }
                });
        return sellTickets;
    }
}

Test class

//Test class
public class Client {
    public static void main(String[] args) {
        //Get proxy object
        ProxyFactory factory = new ProxyFactory();
        
        SellTickets proxyObject = factory.getProxyObject();
        proxyObject.sell();
    }
}

Using dynamic agents, let's think about the following questions

  • ProxyFactory is not the proxy class mentioned in the proxy mode, but the proxy class is a class generated dynamically in memory during the running program. Check the structure of the proxy class through Alibaba's open source Java diagnostic tool (Arthas)

    package com.sun.proxy;
    
    import com.itheima.proxy.dynamic.jdk.SellTickets;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements SellTickets {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        public $Proxy0(InvocationHandler invocationHandler) {
            super(invocationHandler);
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
                m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
                return;
            }
            catch (NoSuchMethodException noSuchMethodException) {
                throw new NoSuchMethodError(noSuchMethodException.getMessage());
            }
            catch (ClassNotFoundException classNotFoundException) {
                throw new NoClassDefFoundError(classNotFoundException.getMessage());
            }
        }
    
        public final boolean equals(Object object) {
            try {
                return (Boolean)this.h.invoke(this, m1, new Object[]{object});
            }
            catch (Error | RuntimeException throwable) {
                throw throwable;
            }
            catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final String toString() {
            try {
                return (String)this.h.invoke(this, m2, null);
            }
            catch (Error | RuntimeException throwable) {
                throw throwable;
            }
            catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final int hashCode() {
            try {
                return (Integer)this.h.invoke(this, m0, null);
            }
            catch (Error | RuntimeException throwable) {
                throw throwable;
            }
            catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final void sell() {
            try {
                this.h.invoke(this, m3, null);
                return;
            }
            catch (Error | RuntimeException throwable) {
                throw throwable;
            }
            catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    }
    
 From the above class, we can see the following information:

 - Agent class:($Proxy0)Realized SellTickets. This confirms that the real class and proxy class we mentioned earlier implement the same interface
 - Proxy class( $Proxy0)Passed the anonymous inner class object we provided to the parent class

- What is the execution process of dynamic agent?

 The following is the key code extracted

 ~~~java
 //Agent class dynamically generated during program running
 public final class $Proxy0 extends Proxy implements SellTickets {
     private static Method m3;
 
     public $Proxy0(InvocationHandler invocationHandler) {
         super(invocationHandler);
     }
 
     static {
         m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
     }
 
     public final void sell() {
         this.h.invoke(this, m3, null);
     }
 }
 
 //Dynamic proxy related classes provided by Java
 public class Proxy implements java.io.Serializable {
 	protected InvocationHandler h;
 	 
 	protected Proxy(InvocationHandler h) {
         this.h = h;
     }
 }
 
 //Agent factory class
 public class ProxyFactory {
 
     private TrainStation station = new TrainStation();
 
     public SellTickets getProxyObject() {
         SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
                 station.getClass().getInterfaces(),
                 new InvocationHandler() {
                     
                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 
                         System.out.println("The agency charges some service fees(JDK Dynamic agent mode)");
                         Object result = method.invoke(station, args);
                         return result;
                     }
                 });
         return sellTickets;
     }
 }
 
 
 //Test access class
 public class Client {
     public static void main(String[] args) {
         //Get proxy object
         ProxyFactory factory = new ProxyFactory();
         SellTickets proxyObject = factory.getProxyObject();
         proxyObject.sell();
     }
 }

The execution process is as follows:

  1. Call the sell() method through the proxy object in the test class
  2. According to the polymorphism, the sell method in the proxy class ($Proxy0) is executed
  3. The invoke method of the self implemented class object that calls the InvocationHandler interface in the sell() method of the proxy class ($Proxy0).
  4. The invoke method executes the sell() method in the class to which the real object belongs (TrainStation) through reflection

5.1.2 CGLIB dynamic agent

Also in the above example, we use the CGLIB proxy again

If the SellTickets interface is not defined and only the TrainStation (railway station class) is defined, the JDK cannot be used because the JDK dynamic agent must define the interface and proxy the interface

CGLIB is a powerful and high-performance code production package. It does not implement the proxy provided by the interface class, which provides a good supplement to the dynamic proxy of JDK.

CGLIB is a package provided by a third party. All the coordinates of the jar package need to be introduced

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

The code is as follows

//train station
public class TrainStation {

    public void sell() {
        System.out.println("Train station ticket");
    }
}

//Agent factory
public class ProxyFactory implements MethodInterceptor {

    private TrainStation target = new TrainStation();

    public TrainStation getProxyObject() {
        //Create an Enhancer object, which is similar to the Proxy class of JDK dynamic Proxy. The next step is to set several parameters
        Enhancer enhancer =new Enhancer();
        //Sets the bytecode object of the parent class
        enhancer.setSuperclass(target.getClass());
        //Set callback function
        enhancer.setCallback(this);
        //Create proxy object
        TrainStation obj = (TrainStation) enhancer.create();
        return obj;
    }

    /*
        intercept Method parameter description:
            o :  Proxy object
            method :  Method instances of methods in real objects
            args :  Actual parameters
            methodProxy : Method instance of the method in the proxy object
     */
    public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("The agency charges some service fees(CGLIB Dynamic agent mode)");
        TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
        return result;
    }
}

//Test class
public class Client {
    public static void main(String[] args) {
        //Create proxy factory object
        ProxyFactory factory = new ProxyFactory();
        //Get proxy object
        TrainStation proxyObject = factory.getProxyObject();

        proxyObject.sell();
    }
}

5.1.6 comparison of three agents

  • JDK agent and CGLIB agent

    CGLIB is used to implement dynamic proxy. The bottom layer of CGLIB uses ASM bytecode generation framework and bytecode technology to generate proxy classes. Before JDK1.6, it is more efficient than using JAVA reflection. The only thing to note is that CGLIB cannot proxy classes or methods declaring final. The principle of introducing CGLIB is to dynamically generate objects of proxy classes

    After JDK dynamic agent is gradually optimized in JDK1.6, JDK1.7 and JDK1.8, JDK agent efficiency is higher than CGLib agent efficiency when the number of calls is small. Only when a large number of calls are made, JDK1.6 and JDK1.7 are a little lower than CGLib agent efficiency, but JDK agent efficiency is higher than CGLib agent efficiency when JDK1.8.

    Therefore, if there is an interface, use JDK dynamic proxy, and if there is no interface, use CGLIB proxy.

  • Dynamic agent and static agent

    Compared with static proxy, the biggest advantage of dynamic proxy is that all methods declared in the interface are transferred to one or more methods of the calling processor (invocationHandler.invoke). In this way, when there are a large number of interface methods, we can handle them flexibly, instead of transferring each method like static proxy.

    If a method is added to the interface, the static agent mode processing implementation class needs to implement this method, and all agents also need to implement this method, which increases the complexity of code bits, while the dynamic agent will not have this problem.

5.1.7 advantages and disadvantages

advantage

  • The proxy pattern acts as an intermediary between the client and the target object and protects the target object
  • The proxy object can extend the function of the target object;
  • The proxy mode can separate the client from the target object and reduce the coupling of the system to a certain extent

shortcoming

  • It increases the complexity of the system

5.1.8 usage scenarios

  • Product or Access agent

B agent==

  • Dynamic agent and static agent

    Compared with static proxy, the biggest advantage of dynamic proxy is that all methods declared in the interface are transferred to one or more methods of the calling processor (invocationHandler.invoke). In this way, when there are a large number of interface methods, we can handle them flexibly, instead of transferring each method like static proxy.

    If a method is added to the interface, the static agent mode processing implementation class needs to implement this method, and all agents also need to implement this method, which increases the complexity of code bits, while the dynamic agent will not have this problem.

5.1.7 advantages and disadvantages

advantage

  • The proxy pattern acts as an intermediary between the client and the target object and protects the target object
  • The proxy object can extend the function of the target object;
  • The proxy mode can separate the client from the target object and reduce the coupling of the system to a certain extent

shortcoming

  • It increases the complexity of the system

5.1.8 usage scenarios

  • Product or Access agent

    Control access to an object. If necessary, different users can be provided with different domains of permissions.

Posted by udendra on Sun, 28 Nov 2021 06:52:07 -0800