浏览 13792 次
锁定老帖子 主题:获取方法参数名称
精华帖 (10) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-05-02
最后修改:2010-05-02
如果一个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] 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-05-02
看了题目,正是困扰我很久的问题!网上也没找到答案!
先投精华票,再细看,待会再评! |
|
返回顶楼 | |
发表时间:2010-05-02
请问一下,上面那个打开Class文件的那个是什么软件?
原来Java没有API可以解析出方法参数名的,难怪我一直找不到。也就是你说的“方法参数类似局部变量一样……”,从外面自然解析不出方法内的局部变量。 Struts、Hibernate这些框架下都有javassist这个jar包,原来这么有用!有空得研究下。 |
|
返回顶楼 | |
发表时间:2010-05-02
rongxh2010 写道 请问一下,上面那个打开Class文件的那个是什么软件?
用的是jclasslib,网址在这儿: http://www.ej-technologies.com/products/jclasslib/overview.html 看了一下jclasslib的源码,它在处理字节码方面的结构和javassist非常相似。 |
|
返回顶楼 | |
发表时间:2010-05-04
受到你的启发,想到spring有这样的实现,有三个实现类:
有空在具体看看: AspectJAdviceParameterNameDiscoverer, LocalVariableTableParameterNameDiscoverer, PrioritizedParameterNameDiscoverer |
|
返回顶楼 | |
发表时间:2010-05-05
spring中的LocalVariableTableParameterNameDiscoverer更好用点
|
|
返回顶楼 | |
发表时间:2010-05-05
http://paranamer.codehaus.org/
|
|
返回顶楼 | |
发表时间:2010-05-06
melin 写道 受到你的启发,想到spring有这样的实现,有三个实现类:
有空在具体看看: AspectJAdviceParameterNameDiscoverer, LocalVariableTableParameterNameDiscoverer, PrioritizedParameterNameDiscoverer 看来Spring中还有好多没发现的东东,呵呵 |
|
返回顶楼 | |
发表时间:2010-05-06
mycybyb 写道 http://paranamer.codehaus.org/
这个也不错,通过修改编译后的class文件,把参数名作为元数据保存下来,使得不管编译选项是否保留了LocalVariableTable,都可以获取到方法参数名。 有时间再细看一下。 thanks |
|
返回顶楼 | |
发表时间:2010-06-18
java的class能否转换为CtClass?
|
|
返回顶楼 | |