字节码编程javassist之获取方法参数类型,参数值列表,返回值等
写在前面
本文看下如何获取方法参数名称,参数值,返回值等。
1:代码
- 需要增强的类:
public class ApiTest1 {public Integer strToInt(String str01, String str02) {return Integer.parseInt(str01);}}
- 插桩类
package com.dahuyou.javassist.huohuo.aa;import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.LongConsumer;public class MyDoIt extends ClassLoader {public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();// 获取类和方法CtClass ctClass = pool.get(ApiTest1.class.getName());ctClass.replaceClassName("ApiTest1", "ApiTest12");String clazzName = ctClass.getName();CtMethod ctMethod = ctClass.getDeclaredMethod("strToInt");// 方法信息:methodInfo.getDescriptor();MethodInfo methodInfo = ctMethod.getMethodInfo();// 方法:入参信息CodeAttribute codeAttribute = methodInfo.getCodeAttribute();// 获取局部变量表信息(位置1就是第一次参数,位置2就是第二个参数,以此类推。。。)LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);CtClass[] parameterTypes = ctMethod.getParameterTypes();// 获取局部变量表的长度boolean isStatic = (methodInfo.getAccessFlags() & AccessFlag.STATIC) != 0; // 判断是否为静态方法int parameterSize = isStatic ? attr.tableLength() : attr.tableLength() - 1; // 静态类型取值// 参数的名称List<String> parameterNameList = new ArrayList<>(parameterSize);// 参数的类型List<String> parameterTypeList = new ArrayList<>(parameterSize);// 参数值StringBuilder parameters = new StringBuilder();for (int i = 0; i < parameterSize; i++) {// 非静态类型去掉第一个this参数parameterNameList.add(attr.variableName(i + (isStatic ? 0 : 1)));parameterTypeList.add(parameterTypes[i].getName());if (i + 1 == parameterSize) {parameters.append("$").append(i + 1);} else {parameters.append("$").append(i + 1).append(",");}}// 方法:出参信息CtClass returnType = ctMethod.getReturnType();String returnTypeName = returnType.getName();String parameterNameListValue = parameterNameList.toString();String parameterTypeListValue = parameterTypeList.toString();
// String parametersValue = parameters.toString();
// ctMethod.addLocalVariable("");// System.out.println("参数的名称:" + parameterNameList + ", 参数的类型:" + parameterTypeList + ", 参数的值:" + parameters);
// ctMethod.insertBefore("System.out.println(\"参数的名称:" + parameterNameListValue + ", 参数的类型:" + parameterTypeListValue + ", 参数的值:" + parametersValue + "\");");ctMethod.addLocalVariable("parametersValue", pool.get(String.class.getName()));
// ctMethod.insertBefore("parametersValue=" + parameters.toString() + "; System.out.println(\"参数的名称:" + parameterNameListValue + ", 参数的类型:" + parameterTypeListValue + ", 参数的值:parametersValue;\");");ctMethod.insertBefore("parametersValue=\"\";parametersValue+=$1;parametersValue+=\",\";parametersValue+=$2; System.out.println(\"参数的名称:" + parameterNameListValue + ", 参数的类型:" + parameterTypeListValue + ", 参数的值\"+parametersValue+\";\");");ctMethod.insertBefore("System.out.println(\"返回值类型:" + returnTypeName + "\");");ctMethod.addLocalVariable("returnVal", pool.get(Integer.class.getName()));
// ctMethod.addLocalVariable("returnVal", CtClass.intType);
// ctMethod.insertAfter("returnVal=$_;System.out.println(\"返回值:\"+returnVal+\"\");");ctMethod.insertAfter("returnVal=$_;");
// ctMethod.insertAfter("System.out.println(\"返回值:\"+returnVal+\"\");");ctMethod.insertAfter("{ com.dahuyou.javassist.huohuo.aa.MyDoIt.show(returnVal);}", false); // 如果返回类型非对象类型,$_ 需要进行类型转换ctMethod.insertAfter("{ com.dahuyou.javassist.huohuo.aa.MyDoIt.show($_);}", false); // 如果返回类型非对象类型,$_ 需要进行类型转换ctClass.writeFile();// 测试调用byte[] bytes = ctClass.toBytecode();Class<?> clazzNew = new MyDoIt().defineClass("com.dahuyou.javassist.huohuo.aa.ApiTest1", bytes, 0, bytes.length);// 反射运行测试Class aClass = clazzNew;Object obj = aClass.newInstance();Method main = aClass.getDeclaredMethod("strToInt", String.class, String.class);main.invoke(obj, "99", "87");// System.out.println("执行耗时:" + (System.nanoTime() - startTime));}// private static void show(Object param) {public static void show(Integer param) {System.out.println("返回值:" + param);}
}
运行:
返回值类型:java.lang.Integer
参数的名称:[str01, str02], 参数的类型:[java.lang.String, java.lang.String], 参数的值99,87;
返回值:99
返回值:99Process finished with exit code 0
需要注意的点:
1:入参信息的获取依赖于局部变量表
2:$_代表方法的返回值,但必须通过方法insertAfter插入到方法结尾,因为只有在结尾处才有返回值这一说