前言
在使用javassist这个库的时候,遇到了很多问题,前前后后花了非常长的时间处理,这里面很多问题是粗心大意造成的,特此总结出来,避免再犯。
1类型全限定名问题
经过实践,大部分自定义类的使用需要使用类的完全限定名。默认包下的不需要(至于javassist默认导入的包是什么,请自行去类路径确认,默认有java.lang等)。
也可以只用javassist导入包,但是导入包后不一定能缩减名称,有时候还是要写完全限定名,所以推荐一律书写类型完全限定名。
//导入包何路径,建议同时存在
ClassPool pool = null;
pool.importPackage("包名或类型完全限定名");
pool.insertClassPath(Class);
//不要这样写。
AOPEvent aopEvent = new AOPEvent();
//应该写作
xxx.xx.AOPEvent aopEvent = new xxx.xx.AOPEvent();
2基本类型装箱拆箱和类型转换问题
javassist中不支持自动装箱和拆箱。装箱和拆箱是JDK编译时的语法糖,在javassist无法利用这样的语法糖,因为它已经是”运行时“。
//在javassist中这样写是错误的
Object o = 5; //JDK会自动装箱,javassist没有所谓这种机制
//javassist 正确写法
int v = 5;
Object o = new Integer(v);
//在javassist中这样写是错误的
Object v = 5;
int n = (int)v; //JDK下类型强转
Object v = 5;
int n = ((Number)v).intValue() //先引用类型之间类型转换,最后通过方法调用得到基本类型的转换
3函数式接口(匿名类)问题
在源码中很容易使用函数式接口编程,因为JDK替我们做了很多事,例如生成类。
但是在javassist中不行,必须规规矩矩的先声明函数式接口的实现类,然后在使用这个类型。
//这样是不行的,这样会报AST错误,javassist解析不了这样的语法
Callable callable = new Callable() {
@Override
public Object call() throws Exception {
return null;
}
};
//只能这样
//第一步先生成
class MyCallable implements Callable{
public Object call() throws Exception {
return null;
}
}
//第二部使用
Callable callable = new MyCallable ();
4JVM参数问题
使用javassist 产生的Class,在没被加载时,很多问题是暴露不出来的,然后类的加载又分开启验证和不开启验证两种模式。如果不开启验证,则有很多问题会被忽略,他们并不影响初始化时的运行,(例如这篇文章中举例的问题)但是等到使用阶段运行的时候(类正在加载的时候),则会引发严重问题,这都是因为略过了类加载验证这个环节,
所以建议强制开启JVM参数 -Xverity:all。保证类加载时都验证。
可以参考类加载机制与JVM -Xverify参数关系 这篇文章。
5泛型使用问题
javassist高级API中已经明确不推荐使用泛型,所以最好不要使用泛型。JAVA泛型,本身是一个假泛型,更像是一种折中(因为运行时擦除)。所以在要javassist中使用泛型,还不如直接使用Object类型来声明,因为泛型擦除后也是Object。