当前位置: 首页 > news >正文

Javassist初体验

       最初接触javassist是在研究dubbo源码的时候,那会对其的理解还停留在动态生成字节码的位置,可以做动态代理之类的动态化处理。最近由于项目需要扫描springMVC中controller层的注解,同时还需要知道方法入参的名字。基于java反射拿到注解简单,但是要拿到入参名字却需要jdk8才提供支持。但是当前项目整体环境基于jdk7,因此又想起javassist,算是get一个新技能。
基本的代码如下:
private String[] getParamName(Method method) throws Exception {
    CtClass cc = pool.get(method.getDeclaringClass().getName());

    CtClass[] params = new CtClass[method.getParameterTypes().length];
    for(int i = 0; i < method.getParameterTypes().length; i++) {
        params[i] = pool.getCtClass(method.getParameterTypes()[i].getName());
    }

    CtMethod cm = cc.getDeclaredMethod(method.getName(), params);

    MethodInfo methodInfo = cm.getMethodInfo();
    CodeAttribute codeAttribute = methodInfo.getCodeAttribute();

    LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
    String[] paramNames = new String[cm.getParameterTypes().length];
    int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
    for(int i = 0; i < attr.tableLength(); i++) {
        if(attr.index(i) >= pos && attr.index(i) < paramNames.length + pos) {
            paramNames[attr.index(i) - pos] = attr.variableName(i);
        }
    }

    return paramNames;
}
 
关键的代码在于下面那个for循环。
先看看Java虚拟机规范的描述:
Java虚拟机使用局部变量表来完成方法调用时的参数传递,当一个方法被调用的时候,它的参数将会传递至从0开始的连续的局部变量表位置上。特别地,当一个实例方法被调用的时候,第0个局部变量一定是用来存储被调用的实例方法所在的对象的引用(即Java语言中的“this”关键字)。后续的其他参数将会传递至从1开始的连续的局部变量表位置上。
 

但此描述中第 0 个局部变量是指 slot 排号为 0,而不是位置为 0。

所以正确的做法就是遍历本地变量表,根据 slot 值确认是不是方法参数,体现在代码中的 API 就是 attr.index(i) 将会返回 slot。

pos 变量主要用于处理实例方法前面的 this 变量,静态方法 pos 将为 0。
解决方案引用自: http://lzxz1234.github.io/java/2014/07/25/Get-Method-Parameter-Names-With-Javassist/
ps:
目前遇到一个问题,在调试的时候偶然发现,attr.index()下标不一定按照自然增序添加。比如,0,1,2,3,5,6等。类似这样的顺序。此时,只有从小到大的顺序可以描述参数位置,因此,上述后面的部分代码修正为如下:
TreeMap<Integer, Integer> map = new TreeMap<>();
for(int i = 0; i < attr.tableLength(); i++) {
    map.put(attr.index(i), i);
}
int index = 0;
boolean flag = false;
for(Integer key : map.keySet()) {
    if (!flag) {
        flag = true;
        continue;
    }

    if (index < paramNames.length) {
        paramNames[index++] = attr.variableName(map.get(key));
    } else {
        break;
    }
}
 

转载于:https://www.cnblogs.com/asfeixue/p/4278471.html

相关文章:

  • JavaScript高级程序设计--基本概念--笔记
  • javabean总结
  • Css布局系列-经典三列布局
  • msyql查表报InnoDB错误
  • Nodejs使用TLS
  • Overview
  • puppet注意事项
  • LAMP搭建小结
  • 【AngularJS】—— 5 表单
  • 用显微镜观察cpu芯片内部
  • 怎样获取android手机联系人并按字母展示(三)
  • C/C++产生随机数
  • Linux对文件归档和压缩(学习笔记八)
  • PLSQL转义字符
  • vim 入门
  • canvas绘制圆角头像
  • JavaScript HTML DOM
  • Laravel核心解读--Facades
  • leetcode386. Lexicographical Numbers
  • magento 货币换算
  • nginx 负载服务器优化
  • Promise面试题2实现异步串行执行
  • python3 使用 asyncio 代替线程
  • vue的全局变量和全局拦截请求器
  • Xmanager 远程桌面 CentOS 7
  • 利用jquery编写加法运算验证码
  • 如何实现 font-size 的响应式
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 温故知新之javascript面向对象
  • 一文看透浏览器架构
  • 用Canvas画一棵二叉树
  • 从如何停掉 Promise 链说起
  • 我们雇佣了一只大猴子...
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​香农与信息论三大定律
  • #162 (Div. 2)
  • #单片机(TB6600驱动42步进电机)
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (2.2w字)前端单元测试之Jest详解篇
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (C语言)fgets与fputs函数详解
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (十三)Flask之特殊装饰器详解
  • (顺序)容器的好伴侣 --- 容器适配器
  • (四)c52学习之旅-流水LED灯
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (转)socket Aio demo
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转载)虚函数剖析
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)