设计模式之代理模式-模拟JDK动态代理实现

168 阅读1分钟

实现功能

  • 通过Proxy的newProxyInstance返回代理对象

实现步骤

  • 根据提供的接口类动态拼接字符串源码(动态产生代理)
  • 将生成的字符串源码,调用提供的JDK Compiler API编译源码,产生新的类(代理类)
  • 将动态产生的代理类load到内存当中,产生一个新的对象(代理对象)
  • return 产生的代理对象

代码实现

  • InvocationHandler

    public interface InvocationHandler {
    	Object invoke(Object proxy, Method method) throws Exception;
    }
    
  • Proxy

    import javax.tools.JavaCompiler;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Parameter;
    import java.net.URL;
    import java.net.URLClassLoader;
    
    /**
     * 模拟动态代理
     */
    public class Proxy {
    
        /**
         * 获取换行符
         */
        private static final String rt = System.lineSeparator();
    
        /**
         * 动态代理类名
         */
        private static final String proxyClassName = "$Proxy0";
        /**
         * 动态代理类保存路径
         */
        private static final String proxyClassFilePath = "f:/temp/proxy/";
    
        public static Object newProxyInstance(Class infc, InvocationHandler h) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            //根据接口拼接字符串源代码
            String methodStr = getMethodStr(infc);
            String str = "package cn.tswine.dp.proxy.imitation;" + rt +
                    "import java.lang.reflect.Method;" + rt + rt +
                    "public class " + proxyClassName + " implements " + infc.getName() + "{" + rt +
                    "   cn.tswine.dp.proxy.imitation.InvocationHandler h;" + rt +
                    "   public " + proxyClassName + "(InvocationHandler h) {" + rt +
                    "       this.h = h;" + rt +
                    "   }" + rt +
                    methodStr +
                    "}";
            //将源码写入文件类
            String fileName = proxyClassFilePath + proxyClassName + ".java";
            File file = new File(fileName);
            FileWriter fw = new FileWriter(file);
            fw.write(str);
            fw.flush();
            fw.close();
            //编译源码(JDK Compiler API),产生新的类(代理类)
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(fileName);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            //将代理类load到内存当中,产生一个新的对象(代理对象)
            URL[] urls = new URL[]{new URL("file:/"+proxyClassFilePath)};
            URLClassLoader ul = new URLClassLoader(urls);
            Class clazz = ul.loadClass("cn.tswine.dp.proxy.imitation.$Proxy0" );
            //return 生成的代理对象
            Constructor ctr = clazz.getConstructor(InvocationHandler.class);
            Object proxy = ctr.newInstance(h);
            return proxy;
    
        }
    
        /**
         * 获取方法字符串
         *
         * @param infc 接口
         * @return
         * @desc 未考虑导入外部包的情况, 未考虑参数问题
         */
        private static String getMethodStr(Class infc) {
    
            //获取接口的所有方法
            Method[] methods = infc.getMethods();
            StringBuilder sbMethod = new StringBuilder();
            for (Method method : methods) {
                String returnType = method.getReturnType().getTypeName();
                boolean returnFlag = returnType.equalsIgnoreCase("void") ? false : true;
                sbMethod.append("@Override");
                sbMethod.append(rt);
                sbMethod.append("public " + returnType + " " + method.getName() + getMethodParams(method) + " {" + rt);
                sbMethod.append("   try {" + rt);
                sbMethod.append("   Method md = " + infc.getName() + ".class.getMethod(\"" + method.getName() + "\");" + rt);
                if (!returnFlag) {
                    sbMethod.append("   h.invoke(this, md);" + rt);
                } else {
                    sbMethod.append("   return  (" + returnType + ")h.invoke(this, md);" + rt);
                }
    
                sbMethod.append("   }catch(Exception e) {e.printStackTrace();}" + rt);
                if (returnFlag) {
                    sbMethod.append(" return null;" + rt);
                }
                sbMethod.append("}" + rt);
            }
            return sbMethod.toString();
        }
    
        /**
         * 获取方法参数
         *
         * @param method
         * @return
         */
        private static String getMethodParams(Method method) {
            StringBuilder sb = new StringBuilder();
            Parameter[] parameters = method.getParameters();
            sb.append("(");
            if (parameters != null && parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    sb.append(parameter.getType().getName() + " ");
                    sb.append(parameter.getName());
                    if ((i + 1) < parameters.length) {
                        sb.append(",");
                    }
                }
            }
            sb.append(")");
            return sb.toString();
        }
    }
    

客户端使用

  • TimeInvocationHandler
    public class TimeInvocationHandler implements InvocationHandler {
    
        private Object target;
    
        public TimeInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method) throws Exception {
            long start = System.currentTimeMillis();
            Object invoke = method.invoke(target);
            long end = System.currentTimeMillis();
            System.out.println("request time :" + (end - start));
            return invoke;
        }
    }
    
  • Client
    public class Client {
    
        public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
            RealHttpApi realHttpApi = new RealHttpApi();
            InvocationHandler invocationHandler = new TimeInvocationHandler(realHttpApi);
            HttpApi2 proxy = (HttpApi2) Proxy.newProxyInstance(HttpApi2.class, invocationHandler);
            String response = proxy.request();
            System.out.println("response:" + response);
        }
    }