前言

在使用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。