Definition of proxy mode

Definition of the proxy pattern: For some reason an object needs to be provided with a proxy to control access to that object. In this case, the access object is not suitable or cannot directly reference the target object. The proxy object acts as the intermediary between the access object and the target object. The proxy mode is also called the delegate mode.

Why use proxy mode

Mediation isolation: In some cases, a client class may not want or be able to directly reference a delegate object, while a proxy object may act as an intermediary between the client and the delegate, characterized by the fact that the proxy and the delegate classes implement the same interface.

Open and close principle, add functionality: In addition to acting as an intermediary between the client class and the delegate class, we can also extend the functionality of the delegate class by adding additional functionality to the proxy class. In this way, we only need to modify the proxy class without modifying the delegate class, in accordance with the open and close principle of code design. The proxy class is responsible for pre-processing messages for the delegate class, filtering messages, forwarding messages to the delegate class, and post-processing the returned results. The proxy class itself does not actually implement the service, but rather provides specific services by calling the related methods of the delegate class. The real business functions are still implemented by delegate classes, but some common services can be added before and after the implementation of business functions. For example, if we want to add caching and logging to the project, we can use the proxy class to do so without opening the wrapped delegate class.

Advantages and disadvantages of agent mode

The main advantages of the proxy model are:

Proxy mode plays an intermediary role and protects the target object between the client and the target object.

Proxy objects extend the functionality of target objects;

The proxy mode can separate the client from the target and reduce the coupling degree of the system to a certain extent.

The main disadvantages of the proxy model are:

Adding a proxy object between the client and the target object slows down request processing.

Increased the complexity of the system;

Fourth, the structure and implementation of agent mode

There are many ways to implement proxies, such as static proxy, dynamic proxy (JDK dynamic proxy, CGLIB dynamic proxy). Therefore, the following three implementation methods are explained.

Static proxy

The structure of the static proxy pattern is relatively simple, mainly by defining a proxy inheriting the abstract topic to contain the real topic, so as to achieve access to the real topic. The main roles of the proxy mode are as follows:

Abstract Topic (MoveAble) class: Business methods that declare real topics and proxy object implementations through interfaces or abstract classes.

Real Topic (Tank) class: Implements the concrete business in an abstract topic, is the real object represented by the proxy object, and is the object ultimately referenced.

TimeProxy class: Provides the same interface as the real topic, with internal references to the real topic, which can access, control, or extend the functionality of the real topic.

Its structure is shown in the figure.

/** * Simulate tank movement *@author: yangqiang
 * @create: the 2021-02-25 22:01 * /
public interface MoveAble {
    void move(a);
}
Copy the code
/ * * *@author: yangqiang
 * @create: the 2021-02-25 22:02 * /
public class Tank implements MoveAble {
    @Override
    public void move(a) {
        System.out.println("On the move");
        try {
            Thread.sleep(new SecureRandom().nextInt(10000));
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code
/ * * *@author: yangqiang
 * @create: the 2021-02-25 22:05 * /
public class TimeProxy implements MoveAble{
    Tank tank;
    public TimeProxy(a) {
        this.tank = new Tank();
    }

    @Override
    public void move(a) {
        long start = System.currentTimeMillis();
        tank.move();
        long end = System.currentTimeMillis();
        System.out.println("Tank ran"+(end-start)+" ms"); }}Copy the code

public class Test {
    public static void main(String[] args) {
        TimeProxy timeProxy = newTimeProxy(); timeProxy.move(); }}Copy the code

Disadvantages of static proxy: You have to create a proxy class for each proxy object. The workload is too large to manage, and the proxy class has to change when the interface adds new functions

Dynamic proxy (JDK Dynamic Proxy)

Dynamic proxies solve many of the problems of manually creating proxy classes with static proxies, one of which is the JDK’s built-in dynamic proxy. It implements dynamic proxies by implementing its own InvocationHandler, and the actual proxy objects are created dynamically for us by the JDK runtime. Its structure diagram is as follows:

Note that the proxy.newProxyInstance () method takes three arguments: Class[] interfaces: specifies the type of the interface implemented by the target object. InvocationHandler: specifies the dynamic handler. Methods of the event handler are fired when a method of the target object is executed

```java public class LogHandler implements InvocationHandler { Object object; public LogHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(proxy.getClass().getName()); byte[] b = ProxyGenerator.generateProxyClass(proxy.getClass().getSimpleName(), proxy.getClass().getInterfaces()); FileOutputStream out = new FileOutputStream("./" + proxy.getClass().getSimpleName() + ".class"); out.write(b); out.flush(); out.close(); return method.invoke(object, args); } public static MoveAble getProxy(){ return (MoveAble) Proxy.newProxyInstance(Tank.class.getClassLoader(), new Class[]{MoveAble.class}, new LogHandler(new Tank())); }}Copy the code
/** * Simulate tank movement *@author: yangqiang
 * @create: the 2021-02-25 22:01 * /
public interface MoveAble {
    void move(a);
    void eat(a);
}
Copy the code
public class Tank implements MoveAble {
    @Override
    public void move(a) {
        System.out.println("On the move");
        try {
            Thread.sleep(new SecureRandom().nextInt(10000));
        } catch(InterruptedException e) { e.printStackTrace(); }}@Override
    public void eat(a) {
        System.out.println("Fuel the tanks."); }}Copy the code
/** * Separate proxy from proxied object * use JDK dynamic proxy *@author: yangqiang
 * @create: * my / 2021-02-25
public class Test {
    public static void main(String[] args) {
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles"."true");
        Reflection analyzes class properties and methods through binary bytecodeMoveAble proxy = LogHandler.getProxy(); proxy.move(); proxy.eat(); }}Copy the code

JDK Dynamic proxies summary: While dynamic proxies significantly reduce our development tasks compared to static proxies, they also reduce the dependence on business interfaces and reduce coupling. However, JDK built-in dynamic proxies can only support classes that implement interfaces. You can see this in the generated Proxy file. Because Java is single-inheritance, Proxy class objects that are generated inherit from Proxy objects and cannot do other inheritance. So it can only be implemented on interfaces. So only interface classes can be proxy.

public final class $Proxy0 extends Proxy implements MoveAble {
    private static Method m1;
    private static Method m4;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw newUndeclaredThrowableException(var4); }}public final void move(a) throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}public final void eat(a) throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}public final String toString(a) throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}public final int hashCode(a) throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.example.smallwhite.designpatterns.proxy.V3.MoveAble").getMethod("move");
            m3 = Class.forName("com.example.smallwhite.designpatterns.proxy.V3.MoveAble").getMethod("eat");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw newNoClassDefFoundError(var3.getMessage()); }}}Copy the code

3. Dynamic proxy (CGLIB dynamic proxy)

JDK implementation of dynamic proxies requires implementation classes to define business methods through interfaces. For classes without interfaces, how to implement dynamic proxies requires CGLIB. CGLIB uses very low-level bytecode technology to create a subclass of a class and use method interception techniques in the subclass to intercept all calls to the methods of the parent class, weaving into crosscutting logic. But because you inherit, you can’t delegate final modified classes.

We implement the CGLIB dynamic proxy by implementing a MethodInterceptor.

public class CglibProxy implements MethodInterceptor {
  private Object target;

  public Object getInstance(final Object target) {
      this.target = target;
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(this.target.getClass());
      enhancer.setCallback(this);
      return enhancer.create();
  }
  @Override
  public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
      returnmethodProxy.invoke(target,args); }}Copy the code
public final class Tank {
    public void move(a){
        System.out.println("Tank in motion!"); }}Copy the code
public class Test {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        Tank tank =(Tank) cglibProxy.getInstance(newTank()); tank.move(); }}Copy the code

CGLIB proxy summary: Dynamic proxy objects created by CGLIB perform better than dynamic proxy objects created by JDK, but CGLIB takes much longer to create proxy objects than JDK. So for singleton objects, where you don’t need to create objects frequently, using CGLIB is more appropriate than using JDK. And because CGLib uses the dynamic subclass method, it cannot proxy the final modification method.

These are the three proxy implementations. Both JDK dynamic proxies and CGLib dynamic proxies are the basis for implementing Spring AOP. In Spring AOP programming: If the target object added to the container has an implementation interface, use the JDK proxy; If the target object does not implement the interface, use the Cglib proxy.