2
回答
jvm java运行时如何获取方法的参数名和参数值
百度AI开发者大赛带你边学边开发,赢100万奖金,加群:418589053   

我现在想写一个log输出,输出格式大体有这几个:类名,方法名,{{参数名:参数值}, ...}

在java的本身中只提供了 “类名,方法名”这两个获取方法,方法的参数名jdk<1.8时,读字节码;jdk>=1.8时,反射;但是参数值只能是运行时拿到,所以我下了openjdk源码,希望有人能给我指点一下,获取{参数名:参数值}去c++的哪里可以拿到,给我指条路,多谢!

举报
sipt
发帖于3年前 2回/2K+阅
共有2个答案 最后回答: 3年前
这应该分成两个问题,1.如何获取参数值. 2.如何获取参数名,


1.如何获取参数值。这个是运行时的数据,你程序处理下获取就好了。比如写一个代理


2.参数名是在编译的时候就写入到class文件的。,而这些方法的参数在class中就是一个局部变量。class对于局部变量的定义和存储专门有张表。
单纯通过反射目前好像没有办法,通过字节码解析倒是可以
比如下面代码


public static void staticMethod(String args1, String args2) {
}


局部变量表:
        [pc: 0, pc: 1] local: args1 index: 0 type: java.lang.String
        [pc: 0, pc: 1] local: args2 index: 1 type: java.lang.String


pc 0是每个字节码指令的程序计数器。[pc: 0, pc: 1] local: args1 index: 0 type: java.lang.String就是说在程序第0个指令到第1个指令的局部变量数组下标为0的变量类型是String变量名是args1.

public static void nonStaticMethod(String args1, String args2) {
}


局部变量表;
        [pc: 0, pc: 1] local: this index: 0 type: asmtest.Test
        [pc: 0, pc: 1] local: args1 index: 1 type: java.lang.String
        [pc: 0, pc: 1] local: args2 index: 2 type: java.lang.String

这个方法比上面的方法多了一个this。因为这个方法是非静态方法。


所以如果要获取参数名需要解析字节码。这里给你一段代码使用ASM

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public class ReadMethodArgNameClassVisitor extends ClassVisitor {

	public Map<String, List<String>> nameArgMap = new HashMap<String, List<String>>();
	
	public ReadMethodArgNameClassVisitor() {
		super(Opcodes.ASM5);
	}
	
	@Override
	public MethodVisitor visitMethod(int access, String name, String desc,
			String signature, String[] exceptions) {
		Type methodType = Type.getMethodType(desc);
		int len = methodType.getArgumentTypes().length;
        List<String> argumentNames = new ArrayList<String>();
		nameArgMap.put(name, argumentNames);
		ReadMethodArgNameMethodVisitor visitor = new ReadMethodArgNameMethodVisitor(Opcodes.ASM5);
		visitor.argumentNames = argumentNames;
		visitor.argLen = len;
		return visitor;
	}
}

import java.util.List;

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

public class ReadMethodArgNameMethodVisitor extends MethodVisitor {

	public List<String> argumentNames;
	
	public int argLen;
	
	public ReadMethodArgNameMethodVisitor(int api) {
		super(api);
	}

	@Override
	public void visitLocalVariable(String name, String desc, String signature,
			Label start, Label end, int index) {
		if("this".equals(name)) {
			return;
		}
		if(argLen-- > 0) {
			argumentNames.add(name);
		}
	}

}

public class POJO {

	public void say(String message, int times){
	}
	
}


import java.io.IOException;
import java.util.List;
import java.util.Map.Entry;

import org.objectweb.asm.ClassReader;

public class Test {

	public static void main(String... args1) throws IOException {
        ClassReader cr = new ClassReader("POJO");
		ReadMethodArgNameClassVisitor classVisitor = new ReadMethodArgNameClassVisitor();
		cr.accept(classVisitor, 0);
		for(Entry<String, List<String>> entry : classVisitor.nameArgMap.entrySet()) {
		    System.out.println(entry.getKey());
			for(String s : entry.getValue()) {
			    System.out.println("    " + s);
			}
		}
	}

}



使用asm版本是

<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm-all</artifactId>
    <version>5.0.3</version>
</dependency>




这里存在一个隐患,如果有些class文件做了加密混淆吧局部变量表里面的变量名改变了,那就没法获得源码级别的参数名了。

思路错了吧。。。

另外,JDK 1.8 以下,读字节码默认也是获取不到参数名的。

Java的字节码文件默认不存储参数名称。在使用javac编译时,如果开启-g:{vars}选项,可以增加Local variable debugging information。对java方法,参数实际是按照局部变量来存储的,所以可以获取参数名称;但对于java接口中的方法声明,这种方法就无法获取参数名称。
顶部