锁定老帖子 主题:JAVA获取方法参数名的分析(二)
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-05-13
2010年5月13日 魏超
javac和Eclipse编译出来的class文件在传入参数名称上的区别 在上文中可以看出,由javac编译的类,IDE在引用了改类的时候,无法获得方法传入参数原来的命名,只能重新赋予arg0,arg1之类的名字。而由eclipse(MyEclipse)编译出来的class文件,却具有传入参数原来的名字。
为了能进一步清楚这个区别,我们这里借助一个软件来查看class文件:JClassLib
JClassLib 写道
JClassLib不但是一个字节码阅读器而且还包含一个类库允许开发者读取,修改,写入Java Class文件与字节码。
我这里只使用了jclasslib的GUI那个版本。jclasslib作为一个java库还可以有更多更强大的功能。用jclasslib分别打开2个class文件,如下图: 上图可以看出,2个class文件相同的testJar方法下,下面那个testJar方法中还注入了"[1]LocalVariableTable"属性。这个属性通过右边的详细可以看出,存储的就是jarName和yourName2个传入参数的名字。而上面那个javac编译出来的class是没有这个属性的。 对于没有注入参数名称的class,按照我的推断,他应该是根据 上面这个方法中的Descriptor这个标志来判断传入参数是什么类型的,再根据类型给一个默认的名称。
注:上图中用红色框起来的descriptor的意思是 <(参数1数据类型,参数2数据类型) 方法返回类型>
因此,我们使用Eclipse等IDE在调用spring.jar等jar的方法时,代码提示可以正确读取到传入参数的原命名是因为 spring.jar等jar包在编译时就向class文件注入了参数名字。因此,eclipse等IDE也是通过读取.class的字节码来获得方法的参数名称的。
通过网上资料得知,eclipse\myEclipse并没有引用sun默认的javac编译器,而是有自己的实现,因此在编译java文件的时候,由eclipse编译出来的与由javac编译出来的就有所不同。
Eclipse/MyEclipse中 .class没有注入参数名称的jar在 attach source之后的区别 上一节的分析,我们了解到,倘若在生成.class文件的时候没有注入参数名称的信息,那么在引入这个class之后,使用它类的方法时,代码提示的参数就只会是arg0,arg1之类的名称。但当我们对这个引入的jar包进行了 attach source之后,代码提示的时候就可以按照source中的正确参数名称来显示。 是不是attach source之后,IDE向jar包中写入了参数名称的信息? 用jclasslib打开attach source以后的class.可以发现这个.class没有发生变化。那么还有一种可能性,就是进行了这个操作之后,eclipse再进行代码提示的时候,是直接从source中读取参数名称的。为了验证这点,我们修改source中方法的参数名。将jarName改成jarNameTest. 再次使用代码提示,可以看到,原来的参数名字没有发生变化。因此,IDE并没有每次都从sourc中读取参数的名称。而是在attach的时候读取了一次,之后做了缓存。
看到这里,我的第一个反应是eclipse在这个工程的目录下保存了类似“索引”的东西,将方法参数对应的名称保存了下来。针对这个猜测,我监测了attach前后的工程目录情况,如下图:
对于在内存中缓存的可能,我们只要杀死eclipse的进程便可以验证(重启eclipse).结果发现,参数名称变成了修改后的。那么可以看出,这个参数名称的缓存,eclipse是放在自己开辟的一块内存空间内的,只是临时的。在失去了这个缓存之后就会重新读取。
小结 通过上面的分析,我们可以得出一个结论,eclipse等IDE在代码提示时对方法参数名称的获取,有2种途径。 A.当.class文件中持有 参数名称 信息的时候,会直接从class中读取。 B.当没有时,从.java源文件中,通过字节流读取参数名称,然后缓存起来。
因此,我们要模仿IDE的做法,获得一个方法的传入参数名称,我们有上面2种途径。具体的实现,在下一篇心得里会继续分析。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-07-09
是个细心人 ,是一片好文章.
我也关注这个问题. 在我的Web框架中,我需要根据参数名称自动到Request中的parameter中取得参数值. 最后没办法我在每个参数上加了标注信息解决这个问题. 你也可以借鉴一下. public JSONObject updateFunctionMenuTree(@Para("moduleId") String moduleId ,@Para("roleIdCommaStrForMenu") String roleIdCommaStr,@Para("state") String state) { System.out.println(state); List<String> menuIdList=menuAndFunctionDao.getAllMenuId(moduleId); String[] roleIdArr=roleIdCommaStr.split(","); System.out.println(roleIdArr.length); int addItemCount=0; for (int i = 0; i < roleIdArr.length; i++) { addItemCount=securityManager.tempAddSecurityMenuIdForRole(roleIdArr[i], menuIdList); } consoleTreeMenuBuilder.reloadTreeMenu(moduleId); JSONObject object=new JSONObject(); object.put("msg", "成功更新功能菜单树 ! "+(addItemCount==0?"":"\n并为选中角色临时添加了 "+addItemCount+" 项菜单的操作权限 ! ")); return object; } 在JAVA 7.0 中,编译时就不会丢掉参数名称信息了.据说.这个说法源于:http://www.ibm.com/developerworks/cn/java/j-javaroundtable/index.html |
|
返回顶楼 | |
发表时间:2010-07-09
还要说一句,很期待你的本系列的第三篇文章.
|
|
返回顶楼 | |
发表时间:2010-11-11
我这里有2种办法供你选择
1.使用javassist.jar 这个项目可以帮助你解决这个问题,它是一个增强的反射工具。 项目地址http://www.csg.is.titech.ac.jp/~chiba/javassist/ 使用例子http://www.blogjava.net/Hafeyang/archive/2010/10/25/using_powerful_java_reflect_tool_javassist_to_getParameterAnnotations_and_getParameterNames.html 但是这个工具的运行效率没有ASM高。 你也可以使用ASM来扫描Class文件这样获取你要的属性名,前提是你需要对 Class字节码有比较深入的了解。 我更趋向于使用后者,但是ASM的API没有javassist方便。 |
|
返回顶楼 | |
浏览 3745 次