Agent model of design pattern

Keywords: Java Design Pattern Algorithm

Proxy pattern is one of the most widely used design patterns. For example, aop in spring is the practice of proxy pattern. Generally, the dynamic proxy mode is widely used in java, and the common implementation methods are jdk dynamic proxy and cglib dynamic proxy.

Implementing dynamic proxy with jdk

Using jdk to implement dynamic proxy code is relatively simple. It should be noted that dynamic proxy based on jdk must be based on interface. The code is as follows.

Define the interface.

public interface Action {

    public void move();
}

Defines the proxied object.

public class TargetAction implements Action {
    @Override
    public void move() {
        System.out.println("target doing move");
    }
}

Implement InvocationHandler to define the caller.

public class InvocationHandlerAction implements InvocationHandler {
    private Action target;

    public InvocationHandlerAction(Action target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoker doing");
        return method.invoke(target, args);
    }
}

Test code.

public class JdkProxyTest {
    public static void main(String[] args) throws IOException {
        Action targetAction = new TargetAction();
        Action proxyAction = (Action) Proxy.newProxyInstance(targetAction.getClass().getClassLoader(), targetAction.getClass().getInterfaces(), new InvocationHandlerAction(targetAction));
        proxyAction.move();
        //Output bytecode file of dynamically generated proxy class
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Action.class});
        FileOutputStream outputStream = new FileOutputStream(new File("D://$Proxy0.class"));
        outputStream.write(bytes);
        outputStream.flush();
        outputStream.close();
    }
}

According to the above code, a dynamic proxy logic can be implemented. Before and after invokationhandler uses reflection to call the method of the target object, some logic enrichment can be made.

It should be noted that ProxyGenerator is used to output dynamically generated bytecode files, as follows.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import design.mode.proxy.jdk.Action;
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 Action {
    private static Method m1;
    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})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void move() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("design.mode.proxy.jdk.Action").getMethod("move");
            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 new NoClassDefFoundError(var3.getMessage());
        }
    }
}

According to the output bytecode file, we can know that the newly generated proxy class implements the Action interface, that is, the interface we define, and implements the interface method. Looking at the interface method, we find that the method calling logic is mainly the following code, h is the class that implements the InvocationHandler interface, and enters the proxy logic through the invoke method of InvocationHandler, Implement dynamic proxy.

    public final void move() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

The whole generation process of dynamic proxy class can be summarized into the following steps.

  1. According to the interface information, a new. java file of the proxy class is generated

  2. Compile and generate. class files according to. java

  3. The classloader reads the class file information to the jvm

  4. Create a new object and set the InvocationHandler parameter.

Implementation of cglib dynamic proxy

Defines the proxied object. Note that the proxy implementation of cglib does not need to be interface based.

public class TargetAction {
    private String word = "default";

    public TargetAction(){

    }

    public TargetAction(String word){
        this.word = word;
    }

    public void move(){
        System.out.println("target doing move" + word);
    }
}

Define a class that functions like InvocationHandler.

public class CglibCallBackInvocationHandler implements MethodInterceptor{
    private TargetAction targetAction;

    public CglibCallBackInvocationHandler(TargetAction targetAction){
        this.targetAction = targetAction;
    }

    /**
     * InvocationHandler function similar to dynamic agent
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("invoker doinig move");
        //return methodProxy.invokeSuper(o, objects);
        return method.invoke(targetAction, objects);
    }
}

Define proxy class Creator

public class CglibProxyCreator {
    public static <T> T create(Class<T> clazz, MethodInterceptor callbackinvoker){
        Enhancer enhancer = new Enhancer();
        //What kind of proxy is set
        enhancer.setSuperclass(clazz);
        //Set invoker
        enhancer.setCallback(callbackinvoker);
        return (T) enhancer.create();
    }
}

Test code.

public class CglibTest  {
    public static void main(String[] args) {
        TargetAction targetAction = new TargetAction("defintioin word");
        TargetAction cglibProxy = CglibProxyCreator.create(TargetAction.class, new CglibCallBackInvocationHandler(targetAction));
        cglibProxy.move();
    }
}

The basic coding method is basically consistent with the dynamic agent implemented by jdk. The above is the use of the two dynamic agent modes.

Posted by zxiny_chin on Sun, 24 Oct 2021 00:02:25 -0700