`
sswh
  • 浏览: 163857 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

获取方法参数名称

阅读更多
struts2让人感觉很别扭的一个地方:由于Action中的方法都是无参数的,真实的参数要么写在Action的实例变量中,要么从request中读取。
如果一个Action只写一个命令,这还没有什么问题,但为了避免Action类数量的膨胀,我们一般将一个CRUD操作都放在同一个Action类中,用action!method的方式来访问。这时候严重影响方法的可读性。不读完代码通常不知道某个Action的方法需要哪些参数传递给它。
action方法的返回值也存在同样问题。

有时候在想,如果struts2的拦截器能自动选择要执行的方法,并且按照方法的参数名称名称来匹配方法参数就好了。这儿的问题是如何获取到方法参数名称。

好,下面我们来看一下怎么获取方法的参数名称。

我们知道,仅仅从class运行的角度,方法参数名称是没有必要的,方法参数类似局部变量一样,也是按照在局部变量表的偏移位置进行访问。为了调试方便(?)javac在编译时可选将方法参数以及局部变量名称存放在class文件中对应方法的局部变量表LocalVariableTable中。

我们看一个不包含局部变量表的class文件的结构:

再看一下包含局部变量表的class文件结构:

要控制是否生成局部变量表,用javac –g参数选项:

在Eclipse中这样设置:


接下来借助于一款字节码工具来读取局部变量表信息。Javassist是东京工业大学Shigeru Chiba编写的开源的字节码工具包,相对于ASM,性能稍低,但可读性更好,如果用Struts2的话,它已经被包含在xwork-core.jar文件中,所以不需要额外导入了。
package my;

import java.util.Arrays;

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;

@SuppressWarnings("unchecked")
public class Hello {

	public static void main(String[] args) throws Exception {
		// 匹配静态方法
		String[] paramNames = getMethodParamNames(Hello.class, "main", String[].class);
		System.out.println(Arrays.toString(paramNames));
		// 匹配实例方法
		paramNames = getMethodParamNames(Hello.class, "foo", String.class);
		System.out.println(Arrays.toString(paramNames));
		// 自由匹配任一个重名方法
		paramNames = getMethodParamNames(Hello.class, "getMethodParamNames");
		System.out.println(Arrays.toString(paramNames));
		// 匹配特定签名的方法
		paramNames = getMethodParamNames(Hello.class, "getMethodParamNames", Class.class, String.class);
		System.out.println(Arrays.toString(paramNames));
	}

	/**
	 * 获取方法参数名称,按给定的参数类型匹配方法
	 * 
	 * @param clazz
	 * @param method
	 * @param paramTypes
	 * @return
	 * @throws NotFoundException
	 *             如果类或者方法不存在
	 * @throws MissingLVException
	 *             如果最终编译的class文件不包含局部变量表信息
	 */
	public static String[] getMethodParamNames(Class clazz, String method, Class... paramTypes)
			throws NotFoundException, MissingLVException {

		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get(clazz.getName());
		String[] paramTypeNames = new String[paramTypes.length];
		for (int i = 0; i < paramTypes.length; i++)
			paramTypeNames[i] = paramTypes[i].getName();
		CtMethod cm = cc.getDeclaredMethod(method, pool.get(paramTypeNames));
		return getMethodParamNames(cm);
	}

	/**
	 * 获取方法参数名称,匹配同名的某一个方法
	 * 
	 * @param clazz
	 * @param method
	 * @return
	 * @throws NotFoundException
	 *             如果类或者方法不存在
	 * @throws MissingLVException
	 *             如果最终编译的class文件不包含局部变量表信息
	 */
	public static String[] getMethodParamNames(Class clazz, String method) throws NotFoundException, MissingLVException {

		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get(clazz.getName());
		CtMethod cm = cc.getDeclaredMethod(method);
		return getMethodParamNames(cm);
	}

	/**
	 * 获取方法参数名称
	 * 
	 * @param cm
	 * @return
	 * @throws NotFoundException
	 * @throws MissingLVException
	 *             如果最终编译的class文件不包含局部变量表信息
	 */
	protected static String[] getMethodParamNames(CtMethod cm) throws NotFoundException, MissingLVException {
		CtClass cc = cm.getDeclaringClass();
		MethodInfo methodInfo = cm.getMethodInfo();
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
		if (attr == null)
			throw new MissingLVException(cc.getName());

		String[] paramNames = new String[cm.getParameterTypes().length];
		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < paramNames.length; i++)
			paramNames[i] = attr.variableName(i + pos);
		return paramNames;
	}

	/**
	 * 在class中未找到局部变量表信息<br>
	 * 使用编译器选项 javac -g:{vars}来编译源文件
	 * 
	 * @author Administrator
	 * 
	 */
	public static class MissingLVException extends Exception {
		static String msg = "class:%s 不包含局部变量表信息,请使用编译器选项 javac -g:{vars}来编译源文件。";

		public MissingLVException(String clazzName) {
			super(String.format(msg, clazzName));
		}
	}

	static void foo() {
	}

	void foo(String bar) {
	}
}


运行结果如下:
[args]
[bar]
[clazz, method, paramTypes]
[clazz, method]
  • 大小: 46.9 KB
  • 大小: 51.1 KB
  • 大小: 10.3 KB
  • 大小: 64 KB
分享到:
评论
9 楼 jamesqiu 2010-06-18  
java的class能否转换为CtClass?
8 楼 sswh 2010-05-06  
mycybyb 写道
http://paranamer.codehaus.org/


这个也不错,通过修改编译后的class文件,把参数名作为元数据保存下来,使得不管编译选项是否保留了LocalVariableTable,都可以获取到方法参数名。

有时间再细看一下。
thanks
7 楼 sswh 2010-05-06  
melin 写道
受到你的启发,想到spring有这样的实现,有三个实现类:
有空在具体看看:
AspectJAdviceParameterNameDiscoverer, LocalVariableTableParameterNameDiscoverer,
PrioritizedParameterNameDiscoverer


看来Spring中还有好多没发现的东东,呵呵
6 楼 mycybyb 2010-05-05  
http://paranamer.codehaus.org/
5 楼 minzaipiao 2010-05-05  
spring中的LocalVariableTableParameterNameDiscoverer更好用点
4 楼 melin 2010-05-04  
受到你的启发,想到spring有这样的实现,有三个实现类:
有空在具体看看:
AspectJAdviceParameterNameDiscoverer, LocalVariableTableParameterNameDiscoverer,
PrioritizedParameterNameDiscoverer
3 楼 sswh 2010-05-02  
rongxh2010 写道
请问一下,上面那个打开Class文件的那个是什么软件?


用的是jclasslib,网址在这儿:
http://www.ej-technologies.com/products/jclasslib/overview.html

看了一下jclasslib的源码,它在处理字节码方面的结构和javassist非常相似。
2 楼 rongxh2010 2010-05-02  
请问一下,上面那个打开Class文件的那个是什么软件?

原来Java没有API可以解析出方法参数名的,难怪我一直找不到。也就是你说的“方法参数类似局部变量一样……”,从外面自然解析不出方法内的局部变量。

Struts、Hibernate这些框架下都有javassist这个jar包,原来这么有用!有空得研究下。
1 楼 rongxh2010 2010-05-02  
看了题目,正是困扰我很久的问题!网上也没找到答案!
先投精华票,再细看,待会再评!

相关推荐

    Java 8中如何获取参数名称的方法示例

    在Java编程语言中,获取方法参数名称通常是一个挑战,因为Java的字节码(class文件)在编译时默认不保存参数的名称。然而,从Java 8开始,Java引入了一个新的特性,允许开发者在运行时通过反射获取方法参数的名称。...

    C# 获取调用函数 参数名称和值

    在C#编程中,获取调用函数的参数名称和值是一项常见的需求,特别是在日志记录、调试或动态处理参数时。下面将详细讲解如何在C#中实现这一功能。 首先,我们要了解C#中的反射机制。反射是.NET框架提供的一种强大工具...

    C# 调用函数时动态获取参数名称和值

    然而,要动态获取参数名称和值,我们需要利用反射(Reflection)和表达式树(Expression Trees)这两个强大的工具。 **反射(Reflection)**是.NET框架的一个特性,它允许程序在运行时检查自身的信息,如类型、属性...

    java实现根据方法查看方法参数名称

    在Java编程中,有时我们需要获取一个方法的参数名称,这对于日志记录、调试或动态代码生成等场景非常有用。然而,Java的常规运行时API并不直接提供这样的功能,因为参数名称在编译后的字节码中通常是不可见的。本文...

    Java获取代码中方法参数名信息的方法

    然后,我们可以通过调用`Parameter`对象的`getName()`方法来获取参数的名称。 以下是一个简单的示例,展示了如何使用Java 8获取方法参数名: ```java public class Test { public void exampleMethod(String ...

    java解析wsdl文档获取方法与参数

    总结,Java解析WSDL文档获取方法与参数涉及到的技术点包括:了解WSDL的基本概念,使用Apache CXF或其他类似库,加载和解析WSDL文档,获取服务、端点、操作和消息信息,最后可能还需要生成Java客户端代码以便于调用...

    jquery获取URL中参数解决中文乱码问题的两种方法

    其中 `name` 是我们要获取参数的名称。正则表达式中的 `i` 标志表示匹配时不区分大小写。 3. 使用 `match` 方法对窗口位置的查询字符串(即URL中“?”后的部分)进行匹配。 4. 如果匹配成功,`unescape` 函数用于将...

    js获取url参数

    - **参数`name`**:表示要获取的查询参数名称。 - **正则表达式`reg`**:用于匹配查询字符串中特定参数的值。 - `(^|&)`: 匹配查询字符串的开头或“&”符号前的位置。 - `name`: 匹配传入的参数名。 - `=([^&]*)`...

    Springmvc请求处理方法参数

    本文将深入探讨Spring MVC请求处理方法的参数特性及其使用方式。 ### 1. 请求方法参数类型 Spring MVC支持多种类型的参数,包括基本数据类型、对象、数组、集合、Map以及自定义类型。例如: - **基本数据类型**:...

    程序启动参数获取工具

    在命令行环境中,我们可以通过在程序名称后面添加参数来改变程序的行为。例如,"myprogram.exe -debug"可以启动程序并开启调试模式。对于桌面应用,虽然用户通常看不到命令行,但很多应用程序依然支持通过快捷方式的...

    发明名称 --- VxWorks的参数化配置方法_参数配置_VxWorks的参数化配置方法_

    获取当前计算机的ID和预设参数配置文件;根据ID,从预设参数配置文件中匹配与ID对应的配置参数;根据配置参数配置VxWorks操作系统,加载应用程序。本发明提供的参数化配置方法,通过根据计算机ID和预设参数配置文件...

    .NET中反射的应用 获取Dll参数

    - `ParameterInfo`类提供了关于方法、构造函数、属性或事件参数的详细信息,如名称、数据类型、参数方向等。 8. **反射在DLL中的应用**: - 加载DLL:`Assembly.LoadFrom(string filePath)`或`Assembly.Load(byte...

    JUnit中获取测试类及方法的名称实现方法

    本文将详细介绍如何使用`TestName`来获取测试类和方法的名称,并讨论相关的编程细节。 首先,`TestName`是`TestWatcher`的一个子类,`TestWatcher`是一个观察者模式的实现,用于在测试方法开始前和结束后执行自定义...

    JS 获取浏览器参数

    在JavaScript中获取浏览器参数是一项常见的任务,这有助于开发者更好地了解用户环境,以便提供更好的兼容性和个性化体验。这篇博文“JS 获取浏览器参数”可能探讨了如何利用JavaScript来收集关于用户浏览器的信息,...

    C#获取串口名称

    #### 获取串口名称的方法 要获取串口名称,我们可以使用`ManagementObjectSearcher`类,这是.NET Framework中的一部分,专门用于执行WMI查询。以下代码片段展示了如何实现这一功能: ```csharp using System; ...

    如何用js获取当前url的参数值

    在给定的代码示例中,作者定义了一个名为`request`的函数,该函数接受一个参数`paras`,用于指定想要获取的查询参数名称。函数的主要逻辑如下: 1. **获取完整的URL**:首先通过`location.href`获取当前页面的完整...

    Android编程获取Wifi名称(SSID)的方法

    首先,需要通过调用getSystemService方法,并传入WIFI_SERVICE参数,来获取一个WifiManager实例。以下是具体的代码实现步骤: ```java WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); ...

    java获取系统参数

    在Java编程中,获取系统参数是一项基本任务,用于了解运行Java程序的环境信息。Java提供了`java.lang.System`类的`getProperties()`方法来获取这些信息。这个方法返回一个`Properties`对象,该对象包含了关于Java...

    获取设备参数

    在IT领域,获取设备参数是一项基础且至关重要的任务,它涉及到对硬件和软件系统的深入了解,以便于调试、优化、监控和管理。设备参数通常包括CPU信息、内存容量、硬盘细节、操作系统版本、网络配置以及各种硬件组件...

Global site tag (gtag.js) - Google Analytics