ASM指南翻译-5
2.2.4转换类
图2.7
?
可以通过修改visit方法的其它参数来实现其它转换,而不仅仅是修改类的版本号。例如,你可以给类增加一个借口。当然也可以修改类的名称,但是这需要修改很多东西,而不只是修改visit方法中类的名称。实际上,类名可能在很多地方存在,所有这些出现的地方都需要修改。
?
优化
前面的转换只改变了原始类中的四个字节。尽管如此,通过上面的代码,b1被完整的解析,产生的事件被用来从头构造b2,尽管这样做不高效。另一种高效的方式是直接复制不需要转换的部分到b2,这样就不需要解析这部分同时也不产生对应的事件。ASM会自动地对下面的方法进行优化:
?
如果ClassReader检测到一个MethodVisitor直接被ClassVisitor返回,而这个ClassVisitor(如ClassWriter)是通过accept的参数直接传递给ClassReader,这就意味着这个方法的内容将不会被转换,并且对应用程序也是不可见的。在上面的情形中,ClassReader组件不会解析这个方法的内容,也不会产生对应的事件,而只是在ClassWriter中复制该方法的字节数组。?
这个优化由ClassReader和ClassWriter来执行,如果它们拥有彼此的引用,就像下面的代码:
byte[] b1= ...
ClassReadercr = new ClassReader(b1);
ClassWritercw = new ClassWriter(cr, 0);
ChangeVersionAdapterca = new ChangeVersionAdapter(cw);
cr.accept(ca,0);
byte[] b2= cw.toByteArray();
?
经过优化,上面的代码将比前面例子中的代码快两倍。因为ChangeVersionAdapter没有转换任何方法。对于转换部分或者所有方法而言,这种对速度的提高虽然很小,但确实显著的,可以达到10%到20%。不幸地是,这种优化需要复制在原始类中定义的所有常量到转换后的类中。这对于在转换中增加字段,方法或者指令什么的不是一个问题,但是相对于未优化的情形,这会导致在大的类转换过程中删除或者重命名很多类的元素。因此,这种优化适合于需要添加代码的转换。
?
使用转换后的类
?
转换后的类b2可以保存到磁盘或者被ClassLoader加载,如前面章节描述的。但是在一个ClassLoader中只能转换被该ClassLoader加载的类。如果你想转换所有的类,你需要把转换的代码放置到一个ClassFileTransformer中,该类定义在java.lang.instrment包中(可以参看该报的文档获得详细信息):
public static void premain(StringagentArgs, Instrumentation inst) {
???????? inst.addTransformer(newClassFileTransformer() {
?????????????????? publicbyte[] transform(ClassLoader l, String name, Class c,
??????????????????????????? ProtectionDomaind, byte[] b)throws IllegalClassFormatException {
??????????????????????????? ClassReadercr = new ClassReader(b);
??????????????????????????? ClassWritercw = new ClassWriter(cr, 0);
??????????????????????????? ClassVisitorcv = new ChangeVersionAdapter(cw);
??????????????????????????? cr.accept(cv,0);
??????????????????????????? returncw.toByteArray();
??????????????????????????? }
???????? });
}