java1.8中javassist获取接口函数参数名称

前提条件

在java8中要获取类函数参数名称必须在编译时增加参数

编译器时加上-parameters参数

具体内容详见 java1.8获取类和接口函数参数名称

尝试使用javassist获取接口函数名称

1 添加依赖

  
      org.javassist
      javassist
      3.23.1-GA
    

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

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 运行结果

java1.8中javassist获取接口函数参数名称_第1张图片

 

你可能感兴趣的:(字节码)