Java:用ASM實(shí)現(xiàn)動(dòng)態(tài)代理實(shí)體類

1. 實(shí)體類動(dòng)態(tài)代理的分析

這篇文章不是專門講解 ASM 的,有興趣的可以去了解一下。ASM官方文檔傳送門。感覺英文吃力的可以下載中文文檔 ASM中文文檔

這里我們只需要知道添加 ASM 的依賴

implementation 'org.ow2.asm:asm:7.0'
implementation 'org.ow2.asm:asm-commons:7.0'
implementation 'org.ow2.asm:asm-util:7.0'

在實(shí)現(xiàn)實(shí)體類的動(dòng)態(tài)代理前,我們先要分析接口和實(shí)體類的區(qū)分

  1. 接口的方法默認(rèn)都是 public 的,所有實(shí)現(xiàn)接口的類默認(rèn)都會(huì)全部繼承所有接口;而實(shí)體類的方法有 private、protected、public 和 default 的區(qū)別
  2. 實(shí)現(xiàn)接口可以直接使用默認(rèn)無參構(gòu)造函數(shù);而繼承實(shí)體類有多個(gè)構(gòu)造函數(shù)需要繼承,并且需要制定一個(gè)構(gòu)造函數(shù)來實(shí)例化代理對(duì)象
  3. 接口的方法都不是 final 的;而實(shí)體類的方法可能是 final 的
  4. 接口的方法都不是 static 的;而實(shí)體類的方法可能是 static 的

再梳理一下,模仿JDK的動(dòng)態(tài)代理的設(shè)計(jì)思路,實(shí)現(xiàn)動(dòng)態(tài)代理實(shí)體類所需要的步驟

  1. 定義 InvocationHandler 類,用于代理對(duì)象的方法調(diào)用時(shí)的回調(diào)
  2. 根據(jù) classloader 和 class 判斷是否有緩存,如果有則直接從緩存獲取。否則再次生成class并在同一個(gè) classloader 加載的話會(huì)出現(xiàn)問題
  3. 判斷被代理對(duì)象是否是final的,不是final才進(jìn)行下一步
  4. 用 ASM 新建一個(gè)類,包名和被代理類一致,采用Proxy_前綴命名。設(shè)置代理類的修飾符和繼承關(guān)系
  5. 添加成員變量 InvocationHandler,便于后面方法調(diào)用時(shí)的回調(diào)
  6. 添加成員變量 InvocationHandler 的 setter 方法
  7. 添加構(gòu)造器,繼承自卑代理類的構(gòu)造器,里面只是簡(jiǎn)單的調(diào)用 super()
  8. 添加調(diào)用 InvocationHandler 的 invoke 方法的方法
  9. 添加方法,篩選出 public、projected、default 的方法,方法內(nèi)直接調(diào)用第8步創(chuàng)建的方法
  10. 添加靜態(tài)代碼塊,用于初始化新建的靜態(tài)方法字段
  11. 用 classloader 生成 class,并放入緩存
  12. 根據(jù) class 實(shí)例化對(duì)象,并且調(diào)用 setter 方法傳入 InvocationHandler

使用 ASM 的時(shí)候,有幾點(diǎn)注意事項(xiàng)和技巧提前說明一下

  1. 因?yàn)?ASM 編寫代碼時(shí)非常麻煩,所以盡可能把步驟封裝成 java 方法,然后 ASM 只需要?jiǎng)?chuàng)建這個(gè) java 方法和調(diào)用這個(gè)方法。用 ASM 創(chuàng)建 java 方法是非常方便的,可以用 ASMifier 直接打印出來所需的代碼。
  2. 如果新建或者刪除了成員變量,那么就必須在構(gòu)造方法,也就是 "<init>" 方法中增加或刪除對(duì)對(duì)應(yīng)字段的賦值操作
  3. 如果新建或者刪除了靜態(tài)變量,那么就必須在靜態(tài)代碼塊,也就是 "<clinit>" 方法中增加或刪除對(duì)對(duì)應(yīng)字段的賦值操作
  4. 在局部變量表中,除了 long 和 double 兩種基本類型需要占用兩個(gè)槽外,其他類型一律都只占用一個(gè)槽
  5. 所有基本類型的裝箱和拆箱操作,都必須手動(dòng)完成
  6. 需要入棧常量時(shí),盡量用 BxPUSH 3 來代替 xCONST_3,比如 BIPUSH 3 代替 ICONST_3。這樣便于提高代碼的適配率。
  7. 在 ASM 中,經(jīng)常要用到類名的內(nèi)部名形式(innerName),其實(shí)就是將 "." 替換為了 "/"
  8. 需要計(jì)算類型描述符或方法描述符時(shí),ASM 提供的 Type 類非常好用,基本上可以避免手動(dòng)拼接類型描述符
  9. 如果要讀取方法的所有參數(shù),最好抽象一個(gè) java 方法,參數(shù)為 Object...,然后所有參數(shù)只需要傳參就行了。這里要注意基本類型的裝箱操作

2. 實(shí)現(xiàn)實(shí)體類動(dòng)態(tài)代理

首先模仿 JDK 實(shí)現(xiàn)一個(gè)代理方法的回調(diào)接口

public interface InvocationHandler {

    /**
     * @param proxy 動(dòng)態(tài)生成的代理對(duì)象
     * @param method 調(diào)用的方法
     * @param args 調(diào)用的參數(shù)
     * @return 該方法的返回值
     * @throws Throwable
     */
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

}

然后有一個(gè) ClassVisitor,用于讀取被代理類的一些數(shù)據(jù)

public class TargetClassVisitor extends ClassVisitor {

    private boolean isFinal;
    private List<MethodBean> methods = new ArrayList<>();
    private List<MethodBean> declaredMethods = new ArrayList<>();
    private List<MethodBean> constructors = new ArrayList<>();

    public TargetClassVisitor() {
        super(Proxy.ASM_VERSION);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        if ((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL){
            isFinal = true;
        }
        if (superName != null) {
            List<MethodBean> beans = initMethodBeanByParent(superName);
            if (beans != null && !beans.isEmpty()) {
                for (MethodBean bean : beans) {
                    if (!methods.contains(bean)) {
                        methods.add(bean);
                    }
                }
            }
        }
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        if ("<init>".equals(name)){
            // 構(gòu)造方法
            MethodBean constructor = new MethodBean(access, name, descriptor);
            constructors.add(constructor);
        } else if (!"<clinit>".equals(name)) {
            // 其他方法
            if ((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL
                    || (access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC) {
                return super.visitMethod(access, name, descriptor, signature, exceptions);
            }
            MethodBean methodBean = new MethodBean(access, name, descriptor);
            declaredMethods.add(methodBean);
            if ((access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC) {
                methods.add(methodBean);
            }
        }
        return super.visitMethod(access, name, descriptor, signature, exceptions);
    }

    public boolean isFinal() {
        return isFinal;
    }

    public List<MethodBean> getMethods() {
        return methods;
    }

    public List<MethodBean> getDeclaredMethods() {
        return declaredMethods;
    }

    public List<MethodBean> getConstructors() {
        return constructors;
    }

    private List<MethodBean> initMethodBeanByParent(String superName){
        try {
            if (superName != null && !superName.isEmpty()){
                ClassReader reader = new ClassReader(superName);
                TargetClassVisitor visitor = new TargetClassVisitor();
                reader.accept(visitor, ClassReader.SKIP_DEBUG);
                List<MethodBean> beans = new ArrayList<>();
                for (MethodBean methodBean : visitor.methods) {
                    // 跳過 final 和 static
                    if ((methodBean.access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL
                            || (methodBean.access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC) {
                        continue;
                    }
                    // 只要 public
                    if ((methodBean.access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC) {
                        beans.add(methodBean);
                    }
                }
                return beans;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static class MethodBean {

        public int access;
        public String methodName;
        public String methodDesc;

        public MethodBean() {
        }

        public MethodBean(int access, String methodName, String methodDesc) {
            this.access = access;
            this.methodName = methodName;
            this.methodDesc = methodDesc;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null){
                return false;
            }
            if (!(obj instanceof MethodBean)){
                return false;
            }
            MethodBean bean = (MethodBean) obj;
            if (access == bean.access
                    && methodName != null
                    && bean.methodName != null
                    && methodName.equals(bean.methodName)
                    && methodDesc != null
                    && bean.methodDesc != null
                    && methodDesc.equals(bean.methodDesc)){
                return true;
            }
            return false;
        }
    }
}

最后就是代理類的實(shí)現(xiàn)了,這個(gè)類比較復(fù)雜,從入口方法 newProxyInstance 開始看

public class Proxy {

    public static final int ASM_VERSION = Opcodes.ASM7;
    public static final int ASM_JDK_VERSION = Opcodes.V1_7;

    // 動(dòng)態(tài)生成代理類的前綴
    public static final String PROXY_CLASSNAME_PREFIX = "$Proxy_";
    // 字段名
    private static final String FIELD_INVOCATIONHANDLER = "invocationHandler";
    // 方法名
    private static final String METHOD_SETTER = "setInvocationHandler";
    private static final String METHOD_INVOKE = "invokeInvocationHandler";
    private static final String METHOD_INVOKE_DESC = "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;";
    private static final String METHOD_FIELD_PREFIX = "method";
    // 緩存容器,防止生成同一個(gè)Class文件在同一個(gè)ClassLoader加載崩潰的問題
    private static final Map<String, Class<?>> proxyClassCache = new HashMap<>();

    /**
     * 緩存已經(jīng)生成的代理類的Class,key值根據(jù) classLoader 和 targetClass 共同決定
     */
    private static void saveProxyClassCache(ClassLoader classLoader, Class<?> targetClass, Class<?> proxyClass) {
        String key = classLoader.toString() + "_" + targetClass.getName();
        proxyClassCache.put(key, proxyClass);
    }

    /**
     * 從緩存中取得代理類的Class,如果沒有則返回 null
     */
    private static Class<?> getProxyClassCache(ClassLoader classLoader, Class<?> targetClass) {
        String key = classLoader.toString() + "_" + targetClass.getName();
        return proxyClassCache.get(key);
    }

    /**
     * 返回一個(gè)動(dòng)態(tài)創(chuàng)建的代理類,此類繼承自 targetClass
     *
     * @param classLoader       從哪一個(gè)ClassLoader加載Class
     * @param invocationHandler 代理類中每一個(gè)方法調(diào)用時(shí)的回調(diào)接口
     * @param targetClass       被代理對(duì)象
     * @param targetConstructor 被代理對(duì)象的某一個(gè)構(gòu)造器,用于決定代理對(duì)象實(shí)例化時(shí)采用哪一個(gè)構(gòu)造器
     * @param targetParam       被代理對(duì)象的某一個(gè)構(gòu)造器的參數(shù),用于實(shí)例化構(gòu)造器
     * @return
     */
    public static Object newProxyInstance(ClassLoader classLoader,
                                          InvocationHandler invocationHandler,
                                          Class<?> targetClass,
                                          Constructor<?> targetConstructor,
                                          Object... targetParam) {
        if (classLoader == null || targetClass == null || invocationHandler == null) {
            throw new IllegalArgumentException("argument is null");
        }
        try {
            // 查看是否有緩存
            Class<?> proxyClass = getProxyClassCache(classLoader, targetClass);
            if (proxyClass != null) {
                // 實(shí)例化代理對(duì)象
                return newInstance(proxyClass, invocationHandler, targetConstructor, targetParam);
            }
            // 獲取目標(biāo)類的一些數(shù)據(jù)
            ClassReader reader = new ClassReader(targetClass.getName());
            TargetClassVisitor targetClassVisitor = new TargetClassVisitor();
            reader.accept(targetClassVisitor, ClassReader.SKIP_DEBUG);
            // 判斷是否是FINAL的
            if (targetClassVisitor.isFinal()) {
                throw new IllegalArgumentException("class is final");
            }
            // 開始生成代理類
            ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
            String newClassName = generateProxyClassName(targetClass);
            String newClassInnerName = newClassName.replace(".", "/");
            String targetClassName = targetClass.getName();
            String targetClassInnerName = Type.getInternalName(targetClass);
            // 創(chuàng)建類
            newClass(writer, newClassInnerName, targetClassInnerName);
            // 添加 InvocationHandler 字段
            addField(writer);
            // 添加 InvocationHandler 的setter
            addSetterMethod(writer, newClassInnerName);
            // 添加構(gòu)造器,直接調(diào)用 super
            List<MethodBean> constructors = targetClassVisitor.getConstructors();
            addConstructor(writer, constructors, targetClassInnerName);
            // 添加調(diào)用 InvocationHandler 的方法
            addInvokeMethod(writer, newClassInnerName);
            // 添加繼承的public方法和目標(biāo)類的protected、default方法
            List<MethodBean> methods = targetClassVisitor.getMethods();
            List<MethodBean> declaredMethods = targetClassVisitor.getDeclaredMethods();
            Map<Integer, Integer> methodsMap = new HashMap<>();
            Map<Integer, Integer> declaredMethodsMap = new HashMap<>();
            int methodNameIndex = 0;
            methodNameIndex = addMethod(writer, newClassInnerName, targetClass.getMethods(),
                    methods, true, methodNameIndex, methodsMap);
            addMethod(writer, newClassInnerName, targetClass.getDeclaredMethods(),
                    declaredMethods, false, methodNameIndex, declaredMethodsMap);
            // 添加靜態(tài)代碼塊的初始化
            addStaticInitBlock(writer, targetClassName, newClassInnerName, methodsMap, declaredMethodsMap);
            // 生成二進(jìn)制數(shù)據(jù)
            byte[] bytes = writer.toByteArray();
            // 保存到文件,用于debug調(diào)試
//            File outputFile = new File("/Users/jm/Downloads/Demo/" + newClassInnerName + ".class");
//            save2File(outputFile, bytes);
            // 從指定ClassLoader加載Class
            proxyClass = transfer2Class(classLoader, bytes);
            // 緩存
            saveProxyClassCache(classLoader, targetClass, proxyClass);
            // 實(shí)例化代理對(duì)象
            return newInstance(proxyClass, invocationHandler, targetConstructor, targetParam);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成代理類的類名生成規(guī)則
     */
    private static String generateProxyClassName(Class<?> targetClass) {
        return targetClass.getPackage().getName() + "." + PROXY_CLASSNAME_PREFIX + targetClass.getSimpleName();
    }

    /**
     * 根據(jù)被代理類的構(gòu)造器,構(gòu)造代理類對(duì)象。生成代理類的實(shí)例時(shí)調(diào)用其setter方法
     */
    private static Object newInstance(Class<?> proxyClass,
                                      InvocationHandler invocationHandler,
                                      Constructor<?> targetConstructor,
                                      Object... targetParam) throws Exception {
        Class<?>[] parameterTypes = targetConstructor.getParameterTypes();
        Constructor<?> constructor = proxyClass.getConstructor(parameterTypes);
        Object instance = constructor.newInstance(targetParam);
        Method setterMethod = proxyClass.getDeclaredMethod(METHOD_SETTER, InvocationHandler.class);
        setterMethod.setAccessible(true);
        setterMethod.invoke(instance, invocationHandler);
        return instance;
    }

    /**
     * 創(chuàng)建類
     */
    private static void newClass(ClassWriter writer, String newClassName, String targetClassName) throws Exception {
        int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL;
        writer.visit(ASM_JDK_VERSION, access, newClassName, null, targetClassName, null);
    }

    /**
     * 添加 invocationHandler 字段
     */
    private static void addField(ClassWriter writer) throws Exception {
        FieldVisitor fieldVisitor = writer.visitField(Opcodes.ACC_PRIVATE, FIELD_INVOCATIONHANDLER,
                Type.getDescriptor(InvocationHandler.class), null, null);
        fieldVisitor.visitEnd();
    }

    /**
     * 添加 invocationHandler 的 setter 方法
     */
    private static void addSetterMethod(ClassWriter writer, String owner) throws Exception {
        String methodDesc = "(" + Type.getDescriptor(InvocationHandler.class) + ")V";
        MethodVisitor methodVisitor = writer.visitMethod(Opcodes.ACC_PUBLIC, METHOD_SETTER, methodDesc, null, null);
        methodVisitor.visitCode();
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
        methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, owner, FIELD_INVOCATIONHANDLER,
                Type.getDescriptor(InvocationHandler.class));
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(2, 2);
        methodVisitor.visitEnd();
    }

    /**
     * 添加構(gòu)造器
     */
    private static void addConstructor(ClassWriter writer, List<MethodBean> constructors,
                                       String targetClassInnerName) throws Exception {
        for (MethodBean constructor : constructors) {
            Type[] argumentTypes = Type.getArgumentTypes(constructor.methodDesc);
            MethodVisitor methodVisitor = writer.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
                    constructor.methodDesc, null, null);
            methodVisitor.visitCode();
            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);

            // 對(duì)每一個(gè)參數(shù),都將對(duì)應(yīng)局部變量表的位置入棧
            for (int i = 0; i < argumentTypes.length; i++) {
                Type argumentType = argumentTypes[i];
                if (argumentType.equals(Type.BYTE_TYPE)
                        || argumentType.equals(Type.BOOLEAN_TYPE)
                        || argumentType.equals(Type.CHAR_TYPE)
                        || argumentType.equals(Type.SHORT_TYPE)
                        || argumentType.equals(Type.INT_TYPE)) {
                    methodVisitor.visitVarInsn(Opcodes.ILOAD, i + 1);
                } else if (argumentType.equals(Type.LONG_TYPE)) {
                    methodVisitor.visitVarInsn(Opcodes.LLOAD, i + 1);
                } else if (argumentType.equals(Type.FLOAT_TYPE)) {
                    methodVisitor.visitVarInsn(Opcodes.FLOAD, i + 1);
                } else if (argumentType.equals(Type.DOUBLE_TYPE)) {
                    methodVisitor.visitVarInsn(Opcodes.DLOAD, i + 1);
                } else {
                    methodVisitor.visitVarInsn(Opcodes.ALOAD, i + 1);
                }
            }

            // 調(diào)用super() 構(gòu)造器
            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, targetClassInnerName, "<init>", constructor.methodDesc, false);
            methodVisitor.visitInsn(Opcodes.RETURN);
            methodVisitor.visitMaxs(argumentTypes.length + 1, argumentTypes.length + 1);
            methodVisitor.visitEnd();
        }
    }

    /**
     * 添加調(diào)用 invocationHandler 的 invoke 方法
     */
    private static void addInvokeMethod(ClassWriter writer, String owner) throws Exception {
        MethodVisitor methodVisitor = writer.visitMethod(Opcodes.ACC_PRIVATE | Opcodes.ACC_VARARGS,
                METHOD_INVOKE, METHOD_INVOKE_DESC, null, null);
        methodVisitor.visitCode();
        // 異常處理
        Label label0 = new Label();
        Label label1 = new Label();
        Label label2 = new Label();
        methodVisitor.visitTryCatchBlock(label0, label1, label2, Type.getInternalName(Throwable.class));
        methodVisitor.visitLabel(label0);
        // 取到 invocationHandler 字段并入棧
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        methodVisitor.visitFieldInsn(Opcodes.GETFIELD, owner, FIELD_INVOCATIONHANDLER,
                Type.getDescriptor(InvocationHandler.class));
        // 將三個(gè)參數(shù)對(duì)應(yīng)的局部變量表位置入棧
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 3);
        String handlerName = Type.getInternalName(InvocationHandler.class);
        String handlerMethodName = "invoke";
        String handlerDesc = "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;";
        // 調(diào)用 invocationHandler.invoke 方法
        methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, handlerName, handlerMethodName, handlerDesc, true);
        // 正常返回
        methodVisitor.visitLabel(label1);
        methodVisitor.visitInsn(Opcodes.ARETURN);
        // 異常處理
        methodVisitor.visitLabel(label2);
        methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1,
                new Object[]{Type.getInternalName(Throwable.class)});
        methodVisitor.visitVarInsn(Opcodes.ASTORE, 4);
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 4);
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Throwable.class),
                "printStackTrace", "()V", false);
        methodVisitor.visitInsn(Opcodes.ACONST_NULL);
        methodVisitor.visitInsn(Opcodes.ARETURN);
        methodVisitor.visitMaxs(4, 5);
        methodVisitor.visitEnd();
    }

    /**
     * 添加繼承的方法或目標(biāo)類本身的方法
     */
    private static int addMethod(ClassWriter writer, String newClassInnerName,
                                 Method[] methods, List<MethodBean> methodBeans,
                                 boolean isPublic, int methodNameIndex,
                                 Map<Integer, Integer> map) throws Exception {
        for (int i = 0; i < methodBeans.size(); i++) {
            MethodBean methodBean = methodBeans.get(i);
            // 跳過final 和 static 的方法
            if ((methodBean.access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL
                    || (methodBean.access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC) {
                continue;
            }
            // 滿足指定的修飾符
            int access = -1;
            if (isPublic) {
                // public 方法
                if ((methodBean.access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC) {
                    access = Opcodes.ACC_PUBLIC;
                }
            } else {
                // protected 方法
                if ((methodBean.access & Opcodes.ACC_PROTECTED) == Opcodes.ACC_PROTECTED) {
                    access = Opcodes.ACC_PROTECTED;
                } else if ((methodBean.access & Opcodes.ACC_PUBLIC) == 0
                        && (methodBean.access & Opcodes.ACC_PROTECTED) == 0
                        && (methodBean.access & Opcodes.ACC_PRIVATE) == 0) {
                    access = 0;
                }
            }
            if (access == -1) {
                continue;
            }
            // 匹配對(duì)應(yīng)的方法
            int methodIndex = findSomeMethod(methods, methodBean);
            if (methodIndex == -1) {
                continue;
            }
            // 將新建字段的后綴索引和對(duì)應(yīng)方法數(shù)組真實(shí)的索引連接起來,方便后面初始化靜態(tài)代碼塊時(shí)使用
            map.put(methodNameIndex, methodIndex);
            // 添加method對(duì)應(yīng)的字段
            String fieldName = METHOD_FIELD_PREFIX + methodNameIndex;
            FieldVisitor fieldVisitor = writer.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
                    fieldName, Type.getDescriptor(Method.class), null, null);
            fieldVisitor.visitEnd();
            // 添加方法的調(diào)用
            addMethod(writer, newClassInnerName, methodBean, access, methodNameIndex);
            methodNameIndex++;
        }
        return methodNameIndex;
    }


    /**
     * 實(shí)現(xiàn)方法的調(diào)用
     */
    private static void addMethod(ClassWriter writer, String newClassInnerName,
                                  MethodBean methodBean, int access, int methodNameIndex) throws Exception {
        MethodVisitor methodVisitor = writer.visitMethod(access, methodBean.methodName,
                methodBean.methodDesc, null, null);
        methodVisitor.visitCode();
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        // 區(qū)分靜態(tài)或者是非靜態(tài)方法調(diào)用
        if ((methodBean.access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC) {
            methodVisitor.visitInsn(Opcodes.ACONST_NULL);
        } else {
            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        }
        // 獲取新建的方法字段
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, newClassInnerName,
                METHOD_FIELD_PREFIX + methodNameIndex, Type.getDescriptor(Method.class));
        Type[] argumentTypes = Type.getArgumentTypes(methodBean.methodDesc);
        // 實(shí)例化數(shù)組,容量對(duì)應(yīng)方法的參數(shù)個(gè)數(shù)
        methodVisitor.visitIntInsn(Opcodes.BIPUSH, argumentTypes.length);
        methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Object.class));
        // 計(jì)算局部變量表的位置,其中 double 和 long 占用兩個(gè)槽,其他占用一個(gè)槽
        int start = 1;
        int stop = start;
        // 布局變量表入棧,基本類型需要裝箱
        for (int i = 0; i < argumentTypes.length; i++) {
            Type type = argumentTypes[i];
            if (type.equals(Type.BYTE_TYPE)) {
                stop = start + 1;
                methodVisitor.visitInsn(Opcodes.DUP);
                // 放入數(shù)組的下標(biāo)位置
                methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                // 局部變量表的索引
                methodVisitor.visitVarInsn(Opcodes.ILOAD, start);
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Byte.class),
                        "valueOf", "(B)Ljava/lang/Byte;", false);
                methodVisitor.visitInsn(Opcodes.AASTORE);
            } else if (type.equals(Type.SHORT_TYPE)) {
                stop = start + 1;
                methodVisitor.visitInsn(Opcodes.DUP);
                // 放入數(shù)組的下標(biāo)位置
                methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                // 局部變量表的索引
                methodVisitor.visitVarInsn(Opcodes.ILOAD, start);
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Short.class),
                        "valueOf", "(S)Ljava/lang/Short;", false);
                methodVisitor.visitInsn(Opcodes.AASTORE);
            } else if (type.equals(Type.CHAR_TYPE)) {
                stop = start + 1;
                methodVisitor.visitInsn(Opcodes.DUP);
                // 放入數(shù)組的下標(biāo)位置
                methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                // 局部變量表的索引
                methodVisitor.visitVarInsn(Opcodes.ILOAD, start);
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Character.class),
                        "valueOf", "(C)Ljava/lang/Character;", false);
                methodVisitor.visitInsn(Opcodes.AASTORE);
            } else if (type.equals(Type.INT_TYPE)) {
                stop = start + 1;
                methodVisitor.visitInsn(Opcodes.DUP);
                // 放入數(shù)組的下標(biāo)位置
                methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                // 局部變量表的索引
                methodVisitor.visitVarInsn(Opcodes.ILOAD, start);
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Integer.class),
                        "valueOf", "(I)Ljava/lang/Integer;", false);
                methodVisitor.visitInsn(Opcodes.AASTORE);
            } else if (type.equals(Type.FLOAT_TYPE)) {
                stop = start + 1;
                methodVisitor.visitInsn(Opcodes.DUP);
                // 放入數(shù)組的下標(biāo)位置
                methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                // 局部變量表的索引
                methodVisitor.visitVarInsn(Opcodes.FLOAD, start);
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Float.class),
                        "valueOf", "(F)Ljava/lang/Float;", false);
                methodVisitor.visitInsn(Opcodes.AASTORE);
            } else if (type.equals(Type.DOUBLE_TYPE)) {
                stop = start + 2;
                methodVisitor.visitInsn(Opcodes.DUP);
                // 放入數(shù)組的下標(biāo)位置
                methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                // 局部變量表的索引
                methodVisitor.visitVarInsn(Opcodes.DLOAD, start);
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Double.class),
                        "valueOf", "(D)Ljava/lang/Double;", false);
                methodVisitor.visitInsn(Opcodes.AASTORE);
            } else if (type.equals(Type.LONG_TYPE)) {
                stop = start + 2;
                methodVisitor.visitInsn(Opcodes.DUP);
                // 放入數(shù)組的下標(biāo)位置
                methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                // 局部變量表的索引
                methodVisitor.visitVarInsn(Opcodes.LLOAD, start);
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Long.class),
                        "valueOf", "(J)Ljava/lang/Long;", false);
                methodVisitor.visitInsn(Opcodes.AASTORE);
            } else if (type.equals(Type.BOOLEAN_TYPE)) {
                stop = start + 1;
                methodVisitor.visitInsn(Opcodes.DUP);
                // 放入數(shù)組的下標(biāo)位置
                methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                // 局部變量表的索引
                methodVisitor.visitVarInsn(Opcodes.ILOAD, start);
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Boolean.class),
                        "valueOf", "(Z)Ljava/lang/Boolean;", false);
                methodVisitor.visitInsn(Opcodes.AASTORE);
            } else {
                stop = start + 1;
                methodVisitor.visitInsn(Opcodes.DUP);
                // 放入數(shù)組的下標(biāo)位置
                methodVisitor.visitIntInsn(Opcodes.BIPUSH, i);
                // 局部變量表的索引
                methodVisitor.visitVarInsn(Opcodes.ALOAD, start);
                methodVisitor.visitInsn(Opcodes.AASTORE);
            }
            start = stop;
        }
        // 調(diào)用 invokeInvocationHandler 方法
        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, newClassInnerName,
                METHOD_INVOKE, METHOD_INVOKE_DESC, false);
        // 處理返回情況,基本類型需要拆箱
        Type returnType = Type.getReturnType(methodBean.methodDesc);
        if (returnType.equals(Type.BYTE_TYPE)) {
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Byte.class));
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Byte.class),
                    "byteValue", "()B", false);
            methodVisitor.visitInsn(Opcodes.IRETURN);
        } else if (returnType.equals(Type.BOOLEAN_TYPE)) {
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Boolean.class));
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Boolean.class),
                    "booleanValue", "()Z", false);
            methodVisitor.visitInsn(Opcodes.IRETURN);
        } else if (returnType.equals(Type.CHAR_TYPE)) {
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Character.class));
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Character.class),
                    "charValue", "()C", false);
            methodVisitor.visitInsn(Opcodes.IRETURN);
        } else if (returnType.equals(Type.SHORT_TYPE)) {
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Short.class));
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Short.class),
                    "shortValue", "()S", false);
            methodVisitor.visitInsn(Opcodes.IRETURN);
        } else if (returnType.equals(Type.INT_TYPE)) {
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Integer.class));
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Integer.class),
                    "intValue", "()I", false);
            methodVisitor.visitInsn(Opcodes.IRETURN);
        } else if (returnType.equals(Type.LONG_TYPE)) {
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Long.class));
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Long.class),
                    "longValue", "()J", false);
            methodVisitor.visitInsn(Opcodes.LRETURN);
        } else if (returnType.equals(Type.FLOAT_TYPE)) {
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Float.class));
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Float.class),
                    "floatValue", "()F", false);
            methodVisitor.visitInsn(Opcodes.FRETURN);
        } else if (returnType.equals(Type.DOUBLE_TYPE)) {
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Double.class));
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Double.class),
                    "doubleValue", "()D", false);
            methodVisitor.visitInsn(Opcodes.DRETURN);
        } else if (returnType.equals(Type.VOID_TYPE)) {
            methodVisitor.visitInsn(Opcodes.RETURN);
        } else {
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, returnType.getInternalName());
            methodVisitor.visitInsn(Opcodes.ARETURN);
        }
        methodVisitor.visitMaxs(8, 37);
        methodVisitor.visitEnd();
    }

    /**
     * 添加靜態(tài)初始代碼塊
     */
    private static void addStaticInitBlock(ClassWriter writer, String targetClassName,
                                           String newClassInnerName, Map<Integer, Integer> methodsMap,
                                           Map<Integer, Integer> declaredMethodsMap) throws Exception {
        String exceptionClassName = Type.getInternalName(ClassNotFoundException.class);
        MethodVisitor methodVisitor = writer.visitMethod(Opcodes.ACC_STATIC, "<clinit>",
                "()V", null, null);
        methodVisitor.visitCode();
        // 開始異常處理
        Label label0 = new Label();
        Label label1 = new Label();
        Label label2 = new Label();
        methodVisitor.visitTryCatchBlock(label0, label1, label2, exceptionClassName);
        methodVisitor.visitLabel(label0);
        // 給繼承的方法添加對(duì)應(yīng)的字段初始化
        for (Map.Entry<Integer, Integer> entry : methodsMap.entrySet()) {
            Integer key = entry.getKey();
            Integer value = entry.getValue();
            methodVisitor.visitLdcInsn(targetClassName);
            methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Class.class),
                    "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Class.class),
                    "getMethods", "()[Ljava/lang/reflect/Method;", false);
            methodVisitor.visitIntInsn(Opcodes.BIPUSH, value);
            methodVisitor.visitInsn(Opcodes.AALOAD);
            methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC, newClassInnerName,
                    METHOD_FIELD_PREFIX + key, Type.getDescriptor(Method.class));
        }
        // 給目標(biāo)類本身的方法添加對(duì)應(yīng)的字段初始化
        for (Map.Entry<Integer, Integer> entry : declaredMethodsMap.entrySet()) {
            Integer key = entry.getKey();
            Integer value = entry.getValue();
            methodVisitor.visitLdcInsn(targetClassName);
            methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Class.class),
                    "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Class.class),
                    "getDeclaredMethods", "()[Ljava/lang/reflect/Method;", false);
            methodVisitor.visitIntInsn(Opcodes.BIPUSH, value);
            methodVisitor.visitInsn(Opcodes.AALOAD);
            methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC, newClassInnerName,
                    METHOD_FIELD_PREFIX + key, Type.getDescriptor(Method.class));
        }

        methodVisitor.visitLabel(label1);
        Label label3 = new Label();
        methodVisitor.visitJumpInsn(Opcodes.GOTO, label3);
        methodVisitor.visitLabel(label2);
        methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1,
                new Object[]{exceptionClassName});
        methodVisitor.visitVarInsn(Opcodes.ASTORE, 0);
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, exceptionClassName,
                "printStackTrace", "()V", false);
        methodVisitor.visitLabel(label3);
        methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(2, 1);
        methodVisitor.visitEnd();
    }

    /**
     * 找到相等方法的索引
     */
    private static int findSomeMethod(Method[] methods, MethodBean methodBean) {
        for (int i = 0; i < methods.length; i++) {
            if (equalsMethod(methods[i], methodBean)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 判斷 {@link Method} 和 {@link MethodBean} 是否相等
     */
    private static boolean equalsMethod(Method method, MethodBean methodBean) {
        if (method == null && methodBean == null) {
            return true;
        }
        if (method == null || methodBean == null) {
            return false;
        }
        try {
            if (!method.getName().equals(methodBean.methodName)) {
                return false;
            }
            if (!Type.getReturnType(method).equals(Type.getReturnType(methodBean.methodDesc))) {
                return false;
            }
            Type[] argumentTypes1 = Type.getArgumentTypes(method);
            Type[] argumentTypes2 = Type.getArgumentTypes(methodBean.methodDesc);
            if (argumentTypes1.length != argumentTypes2.length) {
                return false;
            }
            for (int i = 0; i < argumentTypes1.length; i++) {
                if (!argumentTypes1[i].equals(argumentTypes2[i])) {
                    return false;
                }
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private static void save2File(File file, byte[] bytes) {
        try {
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            OutputStream out = new FileOutputStream(file);
            out.write(bytes);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 將字節(jié)數(shù)組轉(zhuǎn)換為 Class
     */
    private static Class<?> transfer2Class(ClassLoader classLoader, byte[] bytes) {
        try {
            Class cl = Class.forName("java.lang.ClassLoader");
            Method defineClassMethod = cl.getDeclaredMethod("defineClass",
                    new Class[]{String.class, byte[].class, int.class, int.class});
            defineClassMethod.setAccessible(true);
            Class<?> clazz = (Class<?>) defineClassMethod.invoke(classLoader,
                    new Object[]{null, bytes, 0, bytes.length});
            return clazz;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

3. 使用方式

使用方法跟JDK的方法類似,只是需要額外指定被代理對(duì)象實(shí)例化的構(gòu)造器,這是因?yàn)閷?shí)體類可能會(huì)有多個(gè)構(gòu)造器。

private static void proxyByASM() {
    try {
        Demo demo = new Demo();
        Class clazz = Demo.class;
        // 指定被代理對(duì)象的構(gòu)造器,內(nèi)部會(huì)自動(dòng)轉(zhuǎn)換為代理對(duì)象的構(gòu)造器
        Constructor constructor = clazz.getConstructor(new Class[]{});
        Object[] constructorParam = new Object[]{};
        // 指定方法回調(diào)的接口
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("before:" + method.getName());
                // 記住這兒是調(diào)用的被代理對(duì)象的方法,所以傳參是 demo 而不是 proxy
                method.setAccessible(true);
                Object result = method.invoke(demo, args);
                System.out.println("after:" + method.getName());
                return result;
            }
        };
        Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), invocationHandler, clazz, constructor, constructorParam);
        // 分別測(cè)試 public、protected、default的方法
        ((Demo) proxy).publicDemo();
        ((Demo) proxy).protectedDemo();
        ((Demo) proxy).defaultDemo();
        // 測(cè)試有返回值的方法
        ((Demo) proxy).haha();
        // 測(cè)試?yán)^承的方法
        ((Demo) proxy).superPublic();
        System.out.println(((Demo) proxy).toString());
    } catch (Exception e) {
        e.printStackTrace();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容