java1.8中javassist获取接口函数参数名称
程序员文章站
2022-06-18 11:11:51
...
前提条件
在java8中要获取类函数参数名称必须在编译时增加参数
编译器时加上-parameters参数
具体内容详见 java1.8获取类和接口函数参数名称
尝试使用javassist获取接口函数名称
1 添加依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.1-GA</version>
</dependency>
2 在编译时增加-parameters参数
3 编写测试接口和测试方法
package js.oop.parameter;
import javassist.*;
import javassist.bytecode.*;
/**
* @Title: IParameterNameJavassist
* @Description: 演示 Javassist 获取接口的函数参数名称
* @author chy
* @date 2018/8/16 16:44
*/
public interface IParameterNameJavassist {
public abstract void test1(String myMsg);
public abstract void test2(String myMsg,int myAge);
public abstract void test3(String myMsg,int myAge, String mySex);
/**
* 获取类方法参数名称
* @param funName
*/
public static void getClassParamterName(String funName){
Class<?> clazz = ParameterNameJavassist.class;
ClassPool pool = ClassPool.getDefault();
try {
CtClass ctClass = pool.get(clazz.getName());
CtMethod ctMethod = ctClass.getDeclaredMethod(funName);
// 使用javassist的反射方法的参数名
MethodInfo methodInfo = ctMethod.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
.getAttribute(LocalVariableAttribute.tag);
if (attr != null) {
int len = ctMethod.getParameterTypes().length;
// 非静态的成员函数的第一个参数是this
int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
System.out.print(funName+" : ");
for (int i = 0; i < len; i++) {
System.out.print(attr.variableName(i + pos) + ' ');
}
System.out.println();
}
System.out.println(funName+"获取结束");
} catch (NotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取接口方法参数名称
* @param clazz
* @param funName
*/
public static void getInterfaceParamterName(Class<?> clazz,String funName){
ClassPool pool = ClassPool.getDefault();
try {
CtClass ctClass = pool.get(clazz.getName());
CtMethod ctMethod = ctClass.getDeclaredMethod(funName);
// 使用javassist的反射方法的参数名
MethodInfo methodInfo = ctMethod.getMethodInfo();
MethodParametersAttribute methodParameters= (MethodParametersAttribute)methodInfo.getAttribute("MethodParameters");
//获取参数的个数
int count =ctMethod.getParameterTypes().length;
CtClass[] pCtClass= ctMethod.getParameterTypes();
for(int i=0;i<count;i++) {
String str = methodParameters.getConstPool().getUtf8Info(ByteArray.readU16bit(methodParameters.get(), i * 4 + 1));
System.out.println("参数名称:" + str + ",参数类型" + pCtClass[i].getName());
}
System.out.println("");
System.out.println(funName+"获取结束");
System.out.println("");
} catch (NotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
/**
* 抽象函数和接口函数
*/
getInterfaceParamterName(IParameterNameJavassist.class,"test1");
getInterfaceParamterName(IParameterNameJavassist.class,"test2");
getInterfaceParamterName(IParameterNameJavassist.class,"test3");
} catch (Exception e) {
System.out.println("获取抽象类/接口函数参数出错");
}
}
}
4 如果使用 getClassParamterName 函数只能获取 类里面的函数参数名称
5 如果使用 getInterfaceParamterName 函数能获取 类以及抽象类以及接口的函数名称
6 实现原理编译时增加-parameters参数后,字节码中增加了 MethodParameters 元信息,只要能读取元信息就行
public abstract void test1(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_ABSTRACT
MethodParameters:
Name Flags
myMsg
public abstract void test2(java.lang.String, int);
descriptor: (Ljava/lang/String;I)V
flags: ACC_PUBLIC, ACC_ABSTRACT
MethodParameters:
Name Flags
myMsg
myAge
public abstract void test3(java.lang.String, int, java.lang.String);
descriptor: (Ljava/lang/String;ILjava/lang/String;)V
flags: ACC_PUBLIC, ACC_ABSTRACT
MethodParameters:
Name Flags
myMsg
myAge
mySex
7 javassist 中虽然实现了 MethodParametersAttribute 类,但是没有实现获取参数名称的功能,这点很坑啊!!!!
8 无奈看源码,参考 LineNumberAttribute 实现方式,获取 LineNumberAttribute 属性的值
public class LineNumberAttribute extends AttributeInfo {
public static final String tag = "LineNumberTable";
............
public int tableLength() {
return ByteArray.readU16bit(this.info, 0);
}
............
public int lineNumber(int i) {
return ByteArray.readU16bit(this.info, i * 4 + 4);
}
............
}
LineNumber 的存储数据格式如下:
public void fun(java.lang.String, int);
descriptor: (Ljava/lang/String;I)V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=3
................................
37: return
LineNumberTable:
line 16: 0
line 17: 37
LocalVariableTable:
Start Length Slot Name Signature
0 38 0 this Ljs/oop/parameter/ParameterNameJavassist;
0 38 1 name1 Ljava/lang/String;
0 38 2 age1 I
MethodParameters:
Name Flags
name1
age1
9 照葫芦画瓢
//取而第一个参数
str= methodParameters.getConstPool().getUtf8Info(ByteArray.readU16bit(methodParameters.get(), 0 * 4 + 1));
System.out.println(str);
//取而第二个参数
str= methodParameters.getConstPool().getUtf8Info(ByteArray.readU16bit(methodParameters.get(), 1 * 4 + 1));
System.out.println(str);
methodParameters.get() 就是 MethodParameters 的元信息,但是是字节数组,需要转换成字符串
如果有谁知道为什么是 n*4+1 请告知我? n是参数序号从0开始,我是实验出来的!!!!!
10 运行结果