Java:用ASM實現動態代理實體類
1. 實體類動態代理的分析
這篇文章不是專門講解 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'
在實現實體類的動態代理前,我們先要分析介面和實體類的區分
- 介面的方法預設都是 public 的,所有實現介面的類預設都會全部繼承所有介面;而實體類的方法有 private、protected、public 和 default 的區別
- 實現介面可以直接使用預設無參建構函式;而繼承實體類有多個建構函式需要繼承,並且需要制定一個建構函式來例項化代理物件
- 介面的方法都不是 final 的;而實體類的方法可能是 final 的
- 介面的方法都不是 static 的;而實體類的方法可能是 static 的
再梳理一下,模仿JDK的動態代理的設計思路,實現動態代理實體類所需要的步驟
- 定義 InvocationHandler 類,用於代理物件的方法呼叫時的回撥
- 根據 classloader 和 class 判斷是否有快取,如果有則直接從快取獲取。否則再次生成class並在同一個 classloader 載入的話會出現問題
- 判斷被代理物件是否是final的,不是final才進行下一步
- 用 ASM 新建一個類,包名和被代理類一致,採用Proxy_字首命名。設定代理類的修飾符和繼承關係
- 新增成員變數 InvocationHandler,便於後面方法呼叫時的回撥
- 新增成員變數 InvocationHandler 的 setter 方法
- 新增構造器,繼承自卑代理類的構造器,裡面只是簡單的呼叫 super()
- 新增呼叫 InvocationHandler 的 invoke 方法的方法
- 新增方法,篩選出 public、projected、default 的方法,方法內直接呼叫第8步建立的方法
- 新增靜態程式碼塊,用於初始化新建的靜態方法欄位
- 用 classloader 生成 class,並放入快取
- 根據 class 例項化物件,並且呼叫 setter 方法傳入 InvocationHandler
使用 ASM 的時候,有幾點注意事項和技巧提前說明一下
- 因為 ASM 編寫程式碼時非常麻煩,所以儘可能把步驟封裝成 java 方法,然後 ASM 只需要建立這個 java 方法和呼叫這個方法。用 ASM 建立 java 方法是非常方便的,可以用 ASMifier 直接打印出來所需的程式碼。
- 如果新建或者刪除了成員變數,那麼就必須在構造方法,也就是 "<init>" 方法中增加或刪除對對應欄位的賦值操作
- 如果新建或者刪除了靜態變數,那麼就必須在靜態程式碼塊,也就是 "<clinit>" 方法中增加或刪除對對應欄位的賦值操作
- 在區域性變量表中,除了 long 和 double 兩種基本型別需要佔用兩個槽外,其他型別一律都只佔用一個槽
- 所有基本型別的裝箱和拆箱操作,都必須手動完成
- 需要入棧常量時,儘量用 BxPUSH 3 來代替 xCONST_3,比如 BIPUSH 3 代替 ICONST_3。這樣便於提高程式碼的適配率。
- 在 ASM 中,經常要用到類名的內部名形式(innerName),其實就是將 "." 替換為了 "/"
- 需要計算型別描述符或方法描述符時,ASM 提供的 Type 類非常好用,基本上可以避免手動拼接型別描述符
- 如果要讀取方法的所有引數,最好抽象一個 java 方法,引數為 Object...,然後所有引數只需要傳參就行了。這裡要注意基本型別的裝箱操作
2. 實現實體類動態代理
首先模仿 JDK 實現一個代理方法的回撥介面
public interface InvocationHandler { /** * @param proxy 動態生成的代理物件 * @param method 呼叫的方法 * @param args 呼叫的引數 * @return 該方法的返回值 * @throws Throwable */ Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
然後有一個 ClassVisitor,用於讀取被代理類的一些資料
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)){ // 構造方法 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; } } }
最後就是代理類的實現了,這個類比較複雜,從入口方法 newProxyInstance 開始看
public class Proxy { public static final int ASM_VERSION = Opcodes.ASM7; public static final int ASM_JDK_VERSION = Opcodes.V1_7; // 動態生成代理類的字首 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"; // 快取容器,防止生成同一個Class檔案在同一個ClassLoader載入崩潰的問題 private static final Map<String, Class<?>> proxyClassCache = new HashMap<>(); /** * 快取已經生成的代理類的Class,key值根據 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); } /** * 返回一個動態建立的代理類,此類繼承自 targetClass * * @param classLoader從哪一個ClassLoader載入Class * @param invocationHandler 代理類中每一個方法呼叫時的回撥介面 * @param targetClass被代理物件 * @param targetConstructor 被代理物件的某一個構造器,用於決定代理物件例項化時採用哪一個構造器 * @param targetParam被代理物件的某一個構造器的引數,用於例項化構造器 * @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) { // 例項化代理物件 return newInstance(proxyClass, invocationHandler, targetConstructor, targetParam); } // 獲取目標類的一些資料 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); // 建立類 newClass(writer, newClassInnerName, targetClassInnerName); // 新增 InvocationHandler 欄位 addField(writer); // 新增 InvocationHandler 的setter addSetterMethod(writer, newClassInnerName); // 新增構造器,直接呼叫 super List<MethodBean> constructors = targetClassVisitor.getConstructors(); addConstructor(writer, constructors, targetClassInnerName); // 新增呼叫 InvocationHandler 的方法 addInvokeMethod(writer, newClassInnerName); // 新增繼承的public方法和目標類的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); // 新增靜態程式碼塊的初始化 addStaticInitBlock(writer, targetClassName, newClassInnerName, methodsMap, declaredMethodsMap); // 生成二進位制資料 byte[] bytes = writer.toByteArray(); // 儲存到檔案,用於debug除錯 //File outputFile = new File("/Users/jm/Downloads/Demo/" + newClassInnerName + ".class"); //save2File(outputFile, bytes); // 從指定ClassLoader載入Class proxyClass = transfer2Class(classLoader, bytes); // 快取 saveProxyClassCache(classLoader, targetClass, proxyClass); // 例項化代理物件 return newInstance(proxyClass, invocationHandler, targetConstructor, targetParam); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 生成代理類的類名生成規則 */ private static String generateProxyClassName(Class<?> targetClass) { return targetClass.getPackage().getName() + "." + PROXY_CLASSNAME_PREFIX + targetClass.getSimpleName(); } /** * 根據被代理類的構造器,構造代理類物件。生成代理類的例項時呼叫其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; } /** * 建立類 */ 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(); } /** * 新增構造器 */ 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); // 對每一個引數,都將對應區域性變量表的位置入棧 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); } } // 呼叫super() 構造器 methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, targetClassInnerName, "<init>", constructor.methodDesc, false); methodVisitor.visitInsn(Opcodes.RETURN); methodVisitor.visitMaxs(argumentTypes.length + 1, argumentTypes.length + 1); methodVisitor.visitEnd(); } } /** * 新增呼叫 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)); // 將三個引數對應的區域性變量表位置入棧 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;"; // 呼叫 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(); } /** * 新增繼承的方法或目標類本身的方法 */ 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; } // 匹配對應的方法 int methodIndex = findSomeMethod(methods, methodBean); if (methodIndex == -1) { continue; } // 將新建欄位的字尾索引和對應方法陣列真實的索引連線起來,方便後面初始化靜態程式碼塊時使用 map.put(methodNameIndex, methodIndex); // 新增method對應的欄位 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(); // 新增方法的呼叫 addMethod(writer, newClassInnerName, methodBean, access, methodNameIndex); methodNameIndex++; } return methodNameIndex; } /** * 實現方法的呼叫 */ 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); // 區分靜態或者是非靜態方法呼叫 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); // 例項化陣列,容量對應方法的引數個數 methodVisitor.visitIntInsn(Opcodes.BIPUSH, argumentTypes.length); methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Object.class)); // 計算區域性變量表的位置,其中 double 和 long 佔用兩個槽,其他佔用一個槽 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); // 放入陣列的下標位置 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); // 放入陣列的下標位置 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); // 放入陣列的下標位置 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); // 放入陣列的下標位置 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); // 放入陣列的下標位置 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); // 放入陣列的下標位置 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); // 放入陣列的下標位置 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); // 放入陣列的下標位置 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); // 放入陣列的下標位置 methodVisitor.visitIntInsn(Opcodes.BIPUSH, i); // 區域性變量表的索引 methodVisitor.visitVarInsn(Opcodes.ALOAD, start); methodVisitor.visitInsn(Opcodes.AASTORE); } start = stop; } // 呼叫 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(); } /** * 新增靜態初始程式碼塊 */ 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); // 給繼承的方法新增對應的欄位初始化 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)); } // 給目標類本身的方法新增對應的欄位初始化 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(); } } /** * 將位元組陣列轉換為 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的方法類似,只是需要額外指定被代理物件例項化的構造器,這是因為實體類可能會有多個構造器。
private static void proxyByASM() { try { Demo demo = new Demo(); Class clazz = Demo.class; // 指定被代理物件的構造器,內部會自動轉換為代理物件的構造器 Constructor constructor = clazz.getConstructor(new Class[]{}); Object[] constructorParam = new Object[]{}; // 指定方法回撥的介面 InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before:" + method.getName()); // 記住這兒是呼叫的被代理物件的方法,所以傳參是 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); // 分別測試 public、protected、default的方法 ((Demo) proxy).publicDemo(); ((Demo) proxy).protectedDemo(); ((Demo) proxy).defaultDemo(); // 測試有返回值的方法 ((Demo) proxy).haha(); // 測試繼承的方法 ((Demo) proxy).superPublic(); System.out.println(((Demo) proxy).toString()); } catch (Exception e) { e.printStackTrace(); } }