Java获取函数参数名称的几种方法

本文介绍了在不同Java版本中获取函数参数名称的多种方法,包括使用ASM和javassist框架、Spring框架提供的工具类、Java 8及以后版本的内置API以及通过自定义注解等方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java获取函数参数名称的几种方法

        JDK1.7及以下版本的API并不能获取到函数的参数名称,需要使用字节码处理框架,如ASM、javassist等来实现,且需要编译器开启输出调试符号信息的参数的-g,在Eclipse中就是要勾选下面的选项:



1、下面给出一个使用ASM获取函数参数名称的例子:

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

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

public class TestMain {

	public static void main(String[] args) {

		Class<?> clazz = TestMain.class;
		try {
			Method method = clazz.getDeclaredMethod("test", String.class,
					int.class);
			String[] pns = getParameterNamesByAsm5(clazz, method);
			System.out.print(method.getName() + " : ");
			for (String parameterName : pns) {
				System.out.print(parameterName + ' ');
			}
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
	}
	
	public static void test(String param1, int param2) {
		System.out.println(param1 + param2);
	}

	public static String[] getParameterNamesByAsm5(Class<?> clazz,
			final Method method) {
		final Class<?>[] parameterTypes = method.getParameterTypes();
		if (parameterTypes == null || parameterTypes.length == 0) {
			return null;
		}
		final Type[] types = new Type[parameterTypes.length];
		for (int i = 0; i < parameterTypes.length; i++) {
			types[i] = Type.getType(parameterTypes[i]);
		}
		final String[] parameterNames = new String[parameterTypes.length];

		String className = clazz.getName();
		int lastDotIndex = className.lastIndexOf(".");
		className = className.substring(lastDotIndex + 1) + ".class";
		InputStream is = clazz.getResourceAsStream(className);
		try {
			ClassReader classReader = new ClassReader(is);
			classReader.accept(new ClassVisitor(Opcodes.ASM5) {
				@Override
				public MethodVisitor visitMethod(int access, String name,
						String desc, String signature, String[] exceptions) {
					// 只处理指定的方法
					Type[] argumentTypes = Type.getArgumentTypes(desc);
					if (!method.getName().equals(name)
							|| !Arrays.equals(argumentTypes, types)) {
						return super.visitMethod(access, name, desc, signature,
								exceptions);
					}
					return new MethodVisitor(Opcodes.ASM5) {
						@Override
						public void visitLocalVariable(String name, String desc,
								String signature, org.objectweb.asm.Label start,
								org.objectweb.asm.Label end, int index) {
							// 非静态成员方法的第一个参数是this
							if (Modifier.isStatic(method.getModifiers())) {
								parameterNames[index] = name;
							} else if (index > 0) {
								parameterNames[index - 1] = name;
							}
						}
					};
				}
			}, 0);
		} catch (IOException e) {
		} finally {
			try {
				if (is != null) {
					is.close();
				}
			} catch (Exception e2) {
			}
		}
		return parameterNames;
	}

}


2、使用javassist获取函数参数名称的例子:

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

public class TestMain {

	public static void main(String[] args) {
		Class<?> clazz = TestMain.class;
		ClassPool pool = ClassPool.getDefault();
		try {
			CtClass ctClass = pool.get(clazz.getName());
			CtMethod ctMethod = ctClass.getDeclaredMethod("test");

			// 使用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("test : ");
				for (int i = 0; i < len; i++) {
					System.out.print(attr.variableName(i + pos) + ' ');
				}
				System.out.println();
			}
		} catch (NotFoundException e) {
			e.printStackTrace();
		}
	}

	public static void test(String param1, int param2) {
		System.out.println(param1 + param2);
	}
}

可以看见直接使用ASM和javassist来获取函数参数名称都还是比较麻烦的

 

3、使用spring-core中的LocalVariableTableParameterNameDiscoverer来获取函数参数名称,它对ASM进行了封装:

import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

public class TestMain {

	public static void main(String[] args) {
		ParameterNameDiscoverer parameterNameDiscoverer = 
				new LocalVariableTableParameterNameDiscoverer();
		try {
			String[] parameterNames = parameterNameDiscoverer
					.getParameterNames(TestMain.class.getDeclaredMethod("test",
							String.class, int.class));
			System.out.print("test : ");
			for (String parameterName : parameterNames) {
				System.out.print(parameterName + ' ');
			}
		} catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		}
	}

	public static void test(String param1, int param2) {
		System.out.println(param1 + param2);
	}
}


4、在Java1.8之后,可以通过反射API java.lang.reflect.Executable.getParameters来获取到方法参数的元信息。这要求在使用编译器时加上-parameters参数,它会在生成的.class文件中额外存储参数的元信息,这会增加class文件的大小。在Eclipse下默认没有使用该编译参数,需要设置:


下面给出一个使用Java8反射函数参数名称的例子:

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class TestMain {

	public static void main(final String[] arguments) throws Exception {
		Class<?> clazz = TestMain.class;
		Method method = clazz.getDeclaredMethod("test", String.class, int.class);
		System.out.print("test : ");
		Parameter[] parameters = method.getParameters();
		for (final Parameter parameter : parameters) {
			if (parameter.isNamePresent()) {
				System.out.print(parameter.getName() + ' ');
			}
		}
	}

	public void test(String param1, int param2) {
		System.out.println(param1 + param2);
	}
}


5、使用注解。

上面介绍的几种方法都需要依赖编译器附加一定的编译参数,才能获取到。如果程序编译不想保留这些调试信息和附加的元数据,或者你开发一个了框架提供给别人使用,可是该框架想要获取用户代码的函数参数名,因为你并不能控制别人怎么编译Java代码,这时怎么提供一种不受编译器影响的途径来确保获取到函数参数名呐?看看spring mvc是怎么做的——使用函数参数注解。下面使用注解来获取参数名的例子:

定义函数参数注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Parameter {
	String value();
}

使用该注解,并反射函数参数名:

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class TestMain {

	public static void main(String[] args) throws Exception {
		Method method = TestMain.class.getMethod("test", String.class, int.class);
		System.out.print("test : ");
		Annotation parameterAnnotations[][] = method.getParameterAnnotations();
		for (int i = 0; i < parameterAnnotations.length; i++) {
			for (Annotation annotation : parameterAnnotations[i]) {
				if (Parameter.class.equals(annotation.annotationType())) {
					System.out.print(((Parameter) annotation).value() + ' ');
				}
			}
		}
	}

	public void test(@Parameter("param1") String param1,
			@Parameter("param2") int param2) {
		System.out.println(param1 + param2);
	}
}




### Nginx 文件逻辑漏洞(CVE-2013-4547) #### 漏洞概述 Nginx 文件逻辑漏洞(CVE-2013-4547)允许攻击者通过精心构造的 URL 请求来绕过访问控制并读取或执行受限资源。此漏洞的根本原因在于 Nginx 错误地解析了带有特定编码字符的 URL,从而导致文件路径处理不当[^1]。 #### 影响范围 该漏洞影响多个版本的 Nginx,在某些配置下可能导致未经授权的文件访问甚至远程代码执行。具体受影响的版本包括但不限于: - Nginx 1.4.x 版本系列 - Nginx 1.5.x 版本系列 (部分) 当 Web 应用程序部署于上述版本之上时,可能存在潜在风险[^3]。 #### 复现过程 为了验证这一漏洞的存在,可以通过上传一个看似无害但实际上包含恶意 PHP 代码的图片文件 `phpinfo.jpg` 来测试。一旦成功上传,攻击者能够修改 HTTP 请求中的参数使服务器错误解释文件扩展,进而触发命令注入行为[^4]。 ```bash curl -X POST http://example.com/upload.php \ -F "file=@/path/to/phpinfo.jpg" ``` 随后发送如下请求可尝试利用漏洞: ```http GET /uploads/phpinfo.jpg%00.php?cmd=id HTTP/1.1 Host: example.com ``` 如果存在漏洞,则返回的结果会显示当前用户的 ID 信息。 #### 安全修复措施 针对 CVE-2013-4547 的防护手段主要包括以下几个方面: - **升级至最新稳定版**:官方已发布更新解决此问题,建议立即应用最新的安全补丁以消除隐患[^2]。 - **手动修补源码**:对于无法即时升级的情况,可以从官方网站下载专门为此漏洞准备的安全补丁,并按照指引完成编译安装流程。 - **加强输入校验**:无论何时都应严格过滤用户提交的数据,特别是涉及文件操作的部分,防止非法字符进入内部处理环节。 - **启用 WAF 防护**:Web Application Firewall 能够识别异常模式并阻止可疑流量到达应用程序层面上游位置。 综上所述,及时采取适当行动可以有效降低遭受此类攻击的风险。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值