手寫JDK動態代理

今天研究了一下動態代理的生成,自己手動的拼接字符串寫了一個,功能基本實現了,代碼懶得優化了。廢話不說直接貼代碼,如有問題,歡迎指點。

package com.yang.proxy.dynamicproxy.proxy.jdk.util;

import org.apache.commons.lang.StringUtils;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @description : 手寫動態代理生成類
 * Java動態代理只能夠對接口進行代理,不能對普通的類進行代理(因爲所有生成的代理類的父類爲Proxy,Java類繼承機制不允許多重繼承)
 * Java動態代理使用Java原生的反射API進行操作,在生成類上比較高效
 * @date : 2019/12/20 16:23
 */
public class MyProxyUtil {
    public static final String line = "\n";
    public static final String tab = "\t";
    public static AtomicInteger atomicInteger = new AtomicInteger(100);

    //手寫的動態代理生成代理類方法
    public static Object getProxy(ClassLoader loader,
                                  Class<?>[] interfaces,
                                  InvocationHandler handler) throws Exception {
        Object proxy = null;
        //Class<?> targetClass = target.getClass();
        //目標類的全限定名
        //String targetName = targetClass.getName();
        //目標類的包名
        String targetackageName = "com.sun.proxy";
        //String targetackageName = targetName.substring(0, targetName.lastIndexOf("."));
        //目標類實現的接口們
        //Class[] targetInterfaces = targetClass.getInterfaces();
        String classContent = "";
        //和目標類同一個包,避免引得的非public的包無權限訪問
        String packageContent = "package " + targetackageName + ";" + line;
        //導入InvocationHandler包,因爲要用到導入InvocationHandler包
        String importContent = "import java.lang.reflect.InvocationHandler;" + line;
        //導入所有接口包
        for (Class targetInterface : interfaces) {
            importContent += "import " + targetInterface.getName() + ";" + line;
        }
        //定義動態代理類類名$Proxy0123456789
        int index = atomicInteger.incrementAndGet();
        String clazzFirstLineContent = "public class $Proxy" + index + " implements ";
        //實現目標類的所有接口
        for (Class targetInterface : interfaces) {
            clazzFirstLineContent += targetInterface.getSimpleName();
        }
        clazzFirstLineContent += "{" + line;
        //設置固定私有屬性InvocationHandler handler
        String filedContent = tab + "private InvocationHandler handler;" + line;
        //構造函數
        String constructorContent = tab + "public $Proxy" + index + " (InvocationHandler handler){ this.handler = handler; }" + line;
        String methodContent = "";
        //遍歷所有接口
        for (Class targetInterface : interfaces) {
            Method[] methods = targetInterface.getDeclaredMethods();
            //實現所有接口的方法
            for (Method method : methods) {
                String returnTypeName = method.getReturnType().getSimpleName();
                String methodName = method.getName();
                Class args[] = method.getParameterTypes();
                String argsContent = null;
                String argsClassContent = null;
                String paramsContent = null;
                int flag = 0;
                for (Class arg : args) {
                    if (argsContent == null) {
                        argsContent = "";
                    }
                    if (argsClassContent == null) {
                        argsClassContent = "";
                    }
                    if (paramsContent == null) {
                        paramsContent = "";
                    }
                    String argTypeName = arg.getSimpleName();
                    argsContent += argTypeName + " p" + flag + ",";
                    paramsContent += "p" + flag + ",";
                    argsClassContent += "p" + flag + ".getClass(),";
                    flag++;
                }
                paramsContent = StringUtils.isEmpty(paramsContent) ? "null" : paramsContent;
                argsClassContent = StringUtils.isEmpty(argsClassContent) ? "null" : argsClassContent;
                String oldArgsContent = argsContent;
                argsContent = StringUtils.isEmpty(argsContent) ? "" : argsContent;
                if (paramsContent != null && paramsContent.endsWith(",")) {
                    paramsContent = paramsContent.substring(0, paramsContent.length() - 1);
                }
                if (argsClassContent != null && argsClassContent.endsWith(",")) {
                    argsClassContent = argsClassContent.substring(0, argsClassContent.length() - 1);
                }
                if (argsContent != null && argsContent.endsWith(",")) {
                    argsContent = argsContent.substring(0, argsContent.length() - 1);
                }
                if ("void".equals(returnTypeName)) {
                    if (oldArgsContent == null) {
                        methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {try{" + line
                                + tab + tab + "handler.invoke(this,Class.forName(\"" + targetInterface.getName() + "\").getMethod(\"" + methodName + "\", " + paramsContent + ")," + paramsContent + ");" + line
                                + tab + "}catch (Throwable var3) {  var3.printStackTrace(); }" +
                                "}" + line;
                    } else {
                        methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ")  {try{" + line
                                + tab + tab + "handler.invoke(this,Class.forName(\"" + targetInterface.getName() + "\").getMethod(\"" + methodName + "\", " + argsClassContent + "),new Object[]{" + paramsContent + "});" + line
                                + tab + "}catch (Throwable var3) {  var3.printStackTrace(); } " + line
                                + tab + "}" + line;
                    }
                } else {
                    if (oldArgsContent == null) {
                        methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {try{" + line
                                + tab + tab + "return  ( " + returnTypeName + ") handler.invoke(this,Class.forName(\"" + targetInterface.getName() + "\").getMethod(\"" + methodName + "\", " + paramsContent + ")," + paramsContent + ");" + line
                                + tab + "}catch (Throwable var3) { var3.printStackTrace(); }"
                                + tab + "return null;}" + line;
                    } else {
                        methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ")  {try{" + line
                                + tab + tab + "return  (" + returnTypeName + ")handler.invoke(this,Class.forName(\"" + targetInterface.getName() + "\").getMethod(\"" + methodName + "\", " + argsClassContent + "),new Object[]{" + paramsContent + "});" + line
                                + tab + "}catch (Throwable var3) { var3.printStackTrace(); } " + line
                                + tab + "return null;}" + line;
                    }
                }
            }
        }
        //拼裝java類字符串
        classContent = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}";
        //生成一個臨時文件放到本地磁盤中,要先確保文件夾存在,不然會報錯
        String diskName = targetackageName.replaceAll("\\.", "\\\\");
        File file = new File("d:\\" + diskName + "\\$Proxy" + index + ".java");
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fw = new FileWriter(file);
            fw.write(classContent);
            fw.flush();
            fw.close();
            //編輯爲.class
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(file);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            //類加載去加載到jvm中
            URL[] urls = new URL[]{new URL("file:D:\\\\")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class clazz = urlClassLoader.loadClass(targetackageName + ".$Proxy" + index);
            //調用構造函數
            Constructor constructor = clazz.getConstructor(InvocationHandler.class);
            //生成代理類
            proxy = constructor.newInstance(handler);
        } catch (Exception e) {
        }
        return proxy;
    }
}
InvocationHandler:
package com.yang.proxy.dynamicproxy.proxy.jdk;

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

/**
 * JDK動態代理
 * 被代理類必須實現接口,因爲Java動態代理只能夠對接口進行代理,不能對普通的類進行代理(因爲所有生成的代理類的父類爲Proxy,Java類繼承機制不允許多重繼承)
 * <p>
 * 就是你給我一個接口,我替你生成一個代理實現類。
 * 爲什麼要傳一個tag呢?完全可以不傳的,只是因爲你想使用它的方法去實現你的邏輯。
 * 我們可以拿到returnType,自定義的返回一個同類型的值,但是你這創建這個值的邏輯(invoke中的邏輯,只有一個returnType你只能創建一個返回對象,但是你得set屬性吧,這個set屬性的邏輯)是定製化的,只能適用某個接口中的某一個方法的實現邏輯,不適用這個接口中的所有方法,更不適用所有接口的所有方法。
 * 所以最好的辦法就是我們拿到接口的一個已有的實現類去執行它裏面的每一個對應接口的實現方法
 * </p>
 * Java動態代理使用Java原生的反射API進行操作,在生成類上比較高效
 *
 * @author tona.sun
 * @date 2019/8/17 17:11
 */
public class MyJdkInvocationHandler implements InvocationHandler {
    private Object tag;

    public MyJdkInvocationHandler(Object tag) {
        this.tag = tag;
    }

    //被代理方法其實執行的是這個方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
       // System.out.println("invoke proxy:" + proxy);
        /*Class<?> returnType = method.getReturnType();
        if ("void".equals(returnType.getName())) {
            return null;
        }*/
        try {
            //執行的是tag中的method,因爲method是用tag獲取的,所以只能用tag去執行
            //後來我改成了method用接口去獲取,這樣就能用proxy去執行了,然後不出所料的發生了內存溢出(循環調用,死循環了)
            //Object invoke = method.invoke(proxy, args);
            Object invoke = method.invoke(tag, args);
            return invoke;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }catch (Throwable e){
            e.printStackTrace();
        }
        return null;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章