Design Patterns - Dynamic Agent Patterns

Posted by dreamcaster on Sun, 30 Jun 2019 01:34:25 +0200

Dynamic agents are used in many low-level frameworks, such as struts, Spring and so on.
Dynamic proxy is mainly used for method enhancement, so that you can enhance some methods without modifying the source code, and do whatever you want before and after method execution.
Because in InvocationHandler's invoke Method, you can directly get the Method object corresponding to the Method being invoked.
Specific applications, such as adding call logs, doing business control, etc.
 
The so-called dynamic proxy refers to the dynamically generated proxy, which does not need us to write, so we can solve many problems of this proxy class, which will greatly reduce our work.
 
Now we simulate the implementation of jdk dynamic proxy:
1. Processing method interface InvocationHandler.java
package com.bjsxt.proxy;
import java.lang.reflect.Method;

public interface InvocationHandler {
    public void invoke(Object o, Method m);
}

2. Processing Method Implementation (Recording Method Runtime) TimeHandler.java

package com.bjsxt.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler{

    private Object target;

    public TimeHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public void invoke(Object o, Method m) {
        long start = System.currentTimeMillis();
        System.out.println("starttime:" + start);
        System.out.println(o.getClass().getName());
        try {
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("time:" + (end-start));
    }

}

3. Proxy.java proxy generation class

package com.bjsxt.proxy;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class Proxy {
    public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception { //JDK6 Complier API, CGLib, ASM
        String methodStr = "";
        String rt = "\r\n";
        
        Method[] methods = infce.getMethods();
        
        for(Method m : methods) {
            methodStr += "@Override" + rt + 
                         "public void " + m.getName() + "() {" + rt +
                         "    try {" + rt +
                         "    Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
                         "    h.invoke(this, md);" + rt +
                         "    }catch(Exception e) {e.printStackTrace();}" + rt +
                        
                         "}";
        }
        
        String src = 
            "package com.bjsxt.proxy;" +  rt +
            "import java.lang.reflect.Method;" + rt +
            "public class $Proxy1 implements " + infce.getName() + "{" + rt +
            "    public $Proxy1(InvocationHandler h) {" + rt +
            "        this.h = h;" + rt +
            "    }" + rt +
            
            
            "    com.bjsxt.proxy.InvocationHandler h;" + rt +
                            
            methodStr +
            "}";
        String fileName = 
            "d:/src/com/bjsxt/proxy/$Proxy1.java";
        File f = new File(fileName);
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();
        
        //use jdk compiler to compile
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(fileName);
        CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();
        fileMgr.close();
        
        //load into memory and create an instance
        URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");
        System.out.println(c);
        
        Constructor ctr = c.getConstructor(InvocationHandler.class);
        Object m = ctr.newInstance(h);


        return m;
    }
}

4. Interface of proxy class Moveable.java

package com.bjsxt.proxy;

public interface Moveable {
    void move();
    
}

5. Proxy class Tank.java

package com.bjsxt.proxy;
import java.util.Random;

public class Tank implements Moveable {
    @Override
    public void move() {
        
        System.out.println("Tank Moving...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }        
    }
       
}

6. Client Code

package com.bjsxt.proxy;

public class Client {
    public static void main(String[] args) throws Exception {
        Tank t = new Tank();
        InvocationHandler h = new TimeHandler(t);
        
        Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class, h);
        
        m.move();
    }
}
//It can realize arbitrary proxy for arbitrary object and arbitrary interface method.

In the Proxy.newProxyInstance(Moveable.class, h) method, the interface and processing method classes are introduced, and the method of the interface can be acquired and invoked by the interface class.

With the processing method class InvocationHandler, you can specify the processing method for the proxy object.

 

Generated proxy class code $Proxy1.java

package com.bjsxt.proxy;
import java.lang.reflect.Method;
public class $Proxy1 implements com.bjsxt.proxy.Moveable{
    public $Proxy1(InvocationHandler h) {
        this.h = h;
    }
    com.bjsxt.proxy.InvocationHandler h;
@Override
public void move() {
    try {
    Method md = com.bjsxt.proxy.Moveable.class.getMethod("move");
    h.invoke(this, md);
    }catch(Exception e) {e.printStackTrace();}
}}

 

More details can be referred to: Java-Horse Soldier Dynamic Agent Model

Topics: Java JDK Struts Spring