JAVA获取方法参数名的分析(一)

关于题目

首先解释一下题目. 我们知道, Java通过反射,可以从一个类得知它有哪些方法,有哪些变量,也可以知道每个方法中有哪几个什么类型的传入参数。但有一个东西反射取不到,那就是我们对方法传入参数的命名。

取得传入参数的名字有什么意义?

对这个问题的探究,源于在写一个测试类时候的需求。假设我们有一个类需要测试,这个类中有数十个方法。为每个方法编写测试类,将耗费大量的时间和精力。因此我有一种想法,就是通过java的反射,获得这个类所有的方法,再通过传入参数的名字和参数类型,来生成一些符合要求的数据进行传入。(能这样生成数据的前提是:这个类的编码需要遵循严格的规范,对参数的命名有统一的标准,同时,这个类应该和某种业务紧密相关,这样,才能通过业务和参数名字,判断应生成什么合适的数据)。如果能做到上面说的,那么对具有数十或数百个方法的类,要测试的话只需要传入这个类就可以了。

存在的问题

根据上面的设想,问题就出现了。获得类的方法,获得类的参数类型,反射都可以做到。但参数名称呢?上网求证,多数人给了直接否定的答案。因为API中根本没有提供相关的方法。但有一些人的观点启发了我。他们提到,IDE(如eclipse,myeclipse)中在编码过程中,调用一个类的方法,在代码提示的时候,ide是可以显示出方法中的参数名字的,如下图:

IDE是怎样做到的呢,如果IDE可以做到,我们是否可以尝试去分析它们的做法,来获得参数名称。

可能的做法

网上找到了一个很直观的方法——通过直接读取.java文件,把类作为一个普通文本,用正则表达式匹配方法,来直接获取参数的名字。

Java代码  

  1. /**
  2. *   @author   zhangc
  3. *
  4. *一个测试程序,用来扫描文件(java文件),找出所有方法的参数列表
  5. */
  6. import java.io.*;
  7. import java.util.regex.*;
  8. public class ScanSource {
  9. static void findArgsList(Object targetSrc) {
  10. /*
  11. * 正则匹配串基本上是这样子分组情况(A(B(c(d))))
  12. * 串是:(\\w+\\s+\\w+\\s*\\(((\\s*\\w+\\s*(\\[
  13. * \\])*\\s*\\s+(\\[\\])*\\s*\\w+\\s*(\\[\\])*,?)+)\\)\\s*\\{) 比如public
  14. * static void findArgsList(Object targetSrc,int []a){
  15. * A是匹配整个方法定义行:这里是:static void findArgsList(Object targetSrc,int []a){
  16. * B是匹配匹配参数列表:这里是Object targetSrc,int []a
  17. * C是匹配一个参数,包括类型和类型名称和逗号:这里是Object targetSrc, D是匹配数组标识符:这里是[]
  18. * 这个串有点bt,水平有限,只能这样
  19. */
  20. Pattern p = Pattern
  21. .compile("(\\w+\\s+\\w+\\s*\\(((\\s*\\w+\\s*(\\[\\])*\\s*\\s+(\\[\\])*\\s*\\w+\\s*(\\[\\])*,?)+)\\)\\s*\\{)");
  22. Matcher m = p.matcher((CharSequence) targetSrc);
  23. // locate the all methord defination
  24. while (m.find()) {
  25. String methodName = m.group(0);
  26. String methodArgName = m.group(1);
  27. String strArgs = m.group(2);
  28. String fourArgs = m.group(3);
  29. System.out.println(methodName + "\n" + methodArgName + "\n" + strArgs + "\n" + fourArgs + "\n");
  30. }
  31. }
  32. public static String LoadTargetFile(String targetFileName) {
  33. String result = null;
  34. try {
  35. FileInputStream fis = new FileInputStream(targetFileName);
  36. // 临时分配10000size给byte数组。
  37. byte[] bufReceived = new byte[10000];
  38. int counts = fis.read(bufReceived);
  39. byte[] bufActual = new byte[counts];
  40. System.arraycopy(bufReceived, 0, bufActual, 0, counts);
  41. result = new String(bufActual);
  42. } catch (FileNotFoundException e) {
  43. e.printStackTrace();
  44. } catch (IOException e) {
  45. e.printStackTrace();
  46. }
  47. return result;
  48. }
  49. public static void main(String[] args) {
  50. String target = LoadTargetFile("src/com/spring/aop/TestAspect.java");
  51. System.out.println(target);
  52. findArgsList(target);
  53. }
  54. }

这个通过正则表达式的类,在我写的一个简单的测试类中,是可以取得参数的值的,但当把它用在我们那个有几十个方法的类的时候,表达式的匹配就失效了,没有得到任何的结果(具体原因可能是正则表达式的错误,没能匹配到一些方法)。同时,这种方法需要有.java这个源文件,而在IDE中引入的常常是.class组成的Jar包。为了进一步了解IDE对方法传入参数名的处理,下面我做了一个测试。

测试IDE对方法传入参数的处理

建立一个工程。在工程中新建如下的一个类:

Java代码  

  1. package testplugin;
  2. public class TestJar {
  3. public void testJar(String jarName, String yourName){
  4. System.out.println("jarName:" + jarName + "|| yourName:" + yourName);
  5. }
  6. }

接着我们用2种方式对这个类打jar包:

1. 用javac编译类文件然后打到jar包中,命名为testPlugin_javac.jar.

2. 用MyEclipse直接对工程进行导出,导出为testPlugin_myeclipse.jar.

(打开2个jar中的TestJar.class文件,会发现2个class文件有差异)。

再建立一个工程,先后将2个jar包引入做实验,可以看到:

1. 引入testPlugin_javac.jar, 调用testJar方法,如下图


 可以看到,2个传入参数失去了原有的名称。

2. 移除上面的包,引入testPlugin_myEclipse.jar, 调用testJar方法,如下图


 可以看到,参数名称被识别出来了。

关键在于,2个jar包中的class文件不同。我们打开2个class文件(我们只是直观的看一下class文件中的变量,所以没有用专用的工具查看):

javac生成的.class:

myelipse直接打出来的.class(实际上就是调用了debug模式编译出来的.class):

2个class文件里下面的部分都有SourceFile块。应该是用来表示这个class文件是从哪个java文件编译来的。我们重点看上面的部分。

可以看到,用普通的javac编译出来的类,方法的传入参数名会被编译器修改,于是上面第一个图里SourceFile以上的部分就找不到jarName和yourName 2个名字。而只有通过-debug模式编译出来的类,它的参数名才能被保存下来。而也就是在.class文件中有保留下来参数名的jar包,在IDE中代码提示才能正确显示出参数名字。

那么说明IDE是否能识别类中的方法名取决于编译过后产生的不同的class文件。那么下一节我们会使用工具来解析这2个class文件来看其中到底有什么不同。

JAVA获取方法参数名的分析(一)

时间: 2024-10-11 22:07:24

JAVA获取方法参数名的分析(一)的相关文章

java如何获取方法参数名

在java中,可以通过反射获取到类.字段.方法签名等相关的信息,像方法名.返回值类型.参数类型.泛型类型参数等,但是不能够获取方法的参数名.在实际开发场景中,有时需要根据方法的参数名做一些操作,比如像spring-mvc中,@RequestParam.@PathVariable注解,如果不指定相应的value属性,默认就是使用方法的参数名做为HTTP请求的参数名,它是怎么做到的呢? 在这样情况下,有两种方法获取方法来解决这种需求,第一种方法是使用注解,在注解中指定对应应的参数名称,在需要使用参数

javassist:增强型的java反射工具,获取方法参数名

java的反射是不能获取方法的参数名的.比如: [java] view plaincopyprint? public String concatString(String str1,String str2){ return str1+str2; }     public String concatString(String str1,String str2){         return str1+str2;     } 想获取"str1",和"str1"这个参数

为何Spring MVC可获取到方法参数名,而MyBatis却不行?【享学Spring MVC】

每篇一句 胡适:多谈些问题,少聊些主义 前言 Spring MVC和MyBatis作为当下最为流行的两个框架,大家平时开发中都在用.如果你往深了一步去思考,你应该会有这样的疑问: 在使用Spring MVC的时候,你即使不使用注解,只要参数名和请求参数的key对应上了,就能自动完成数值的封装 在使用MyBatis(接口模式)时,接口方法向xml里的SQL语句传参时,必须(当然不是100%的必须,特殊情况此处不做考虑)使用@Param('')指定key值,在SQL中才可以取到 我敢相信这绝不是我一

Java 反射理解(三)-- Java获取方法信息

Java 反射理解(三)-- Java获取方法信息 基本的数据类型.void关键字,都存在类类型. 举例如下: public class ClassDemo2 { public static void main(String[] args) { Class c1 = int.class;//int 的类类型 Class c2 = String.class;//String类的类类型,可以理解为String类字节码 Class c3 = double.class; Class c4 = Doubl

J2SE_通过反射能获取方法参数名称吗?

一.Java通过反射可以获取方法的参数名称吗? 不能!!! 二.测试示例代码: import java.lang.reflect.Method; class T { public void print(String username) { System.out.println(username); } } public class Test { public static void main(String[] args) { T t = new T(); t.print("zhangsan&qu

java获取计算机硬件参数

public class HardWareUtils { /**   *   * 获取主板序列号   *   *   *   * @return   */ public static String getMotherboardSN() { String result = ""; try { File file = File.createTempFile("realhowto", ".vbs"); file.deleteOnExit(); File

Java的方法参数-想想挺有趣的问题

一直认为Java的方法参数都是传递值,调用后对本身不影响. Java不存在C/C++中的指针,在快速排序中,传入的数组,却发生了值的改变.由此引发的思考: //为方便举例,以下为部分快速排序伪代码 传入的数组,在递归中,数组值被操作. void quickSort(int s[], int l, int r){ if (l < r){ quickSort(s, l, i - 1); quickSort(s, i + 1, r); } } 结论:Java方法参数中传递的是值,在参数是引用类型(如数

Java调用方法参数究竟是传值还是传址?

之前阅读<Head First Java>的时候,记得里面有提到过,Java在调用方法,传递参数的时候,采用的是pass-by-copy的方法,传递一份内容的拷贝,即传值.举一个最简单的例子: 1 public class Test { 2 public static void main(String[] args) { 3 int numberA = 1; 4 int numberB = 2; 5 swap(numberA, numberB); 6 System.out.println(nu

java maven打包jar 方法参数名变var1,var2之类的无意义参数名怎么办

这是idea 对.class反编译的结果.要想看完整源码,可以使用maven-source-plugin,在pom.xml里配置: <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <executions> <execution> <id>attach-sources