网上的文章都在说,对一个Collection中的各个元素进行访问的时候,用很多的if else和instanceof来区分各个对象,并调用其中的方法,这样很不好看。所以创造了visitor模式。我总觉得这个例子相当不好理解。
其实visitor模式要解决的问题是这样的:
有这样一些对象,他们各自持有一些操作(方法),现在需要在一个方法里调用这些对象的操作,但我不知道这些对象的类型,我只知道我要调用它们上面的操作,例如,如果是对象A,那我就调用A.a(); 如果是对象B,那我就调用B.b()。因此,把这些对象定义为Visitable, 我定义为Visitor。这些对象接受我的访问,因此他们都有一个accept()方法,参数就是我。在我的实现里,分别有调用A.a()和B.b()的方法,命名为visitA()和visitB(). 因此,对象A实现Visitable时,就要在accept()里调用我的visitA()。同理,对象B实现Visitable时,就要在accept()里调用我的visitB()。
原来的代码是这样的:
A{
a();
}
B{
b();
}
我{
访问他们(X){
if(x instanceof A){
((A)x).a();
}
else if(x instanceof B){
((B)x).b();
}
}
之后的代码是这样的。添加两个接口:
Visitable{
accept(Vistor v);
}
Visitor{
visitA(A); // 这是因为我知道要访问A或者B。
visitB(B);
}
原来的3个类变成了上面两个接口的实现类:
A implements Visitable{
accept(Visitor v){
v.visitA();
}
a();
}
B implements Visitable{
accept(Visitor v){
v.visitB();
}
b();
}
我 implements Visitor{
visitA(A){
A.a();
}
visitB(B){
B.b();
}
访问他们(X){
((Visitable)X).accept();
}
}
看着很繁琐吧?只有这个方法“访问他们”得到了简化。。。。但是从编程角度来说,后面的实现虽然代码似乎多了点,但是都很不容易出错,也都体现了OO的思想。
再回到ASM。ASM里最重要的一个类就是ClassVisitor。这是个Visitor。它的具体实现是ClassWriter和ClassAdapter等等。他们负责对Class中的每个元素(Field,Method等)进行访问。那么ASM里的Visitable是什么呢?Visitable就是ClassReader。ClassReader是一个parser,对给定的byte[],它可以把这个byte数组拆分成各个元素。ClassReader.accept()的参数就是ClassVisitor,在accept()中,解析过程被触发,每当解析到给定的元素,就会调用相应的Visitor的方法。
ASM提供的最基本的ClassVisitor,就是ClassWriter和ClassAdapter。
ClassWriter负责生成Class的byte数组。它有两种构造方法:一种只有flag,一种有一个ClassReader作为参数。其实两种没有本质区别,但第二种,用ClassReader作为参数时,constant pool是被copy的,在enhancement主要用来“添加代码”的情况下,可以节省大量的时间。一般bytecode enhancement都是在添加代码,所以很有用。
ClassAdapter是一个空的ClassVisitor,它会把所有的visit方法都delegate到自己所持有的ClassVisitor。而自己持有的这个ClassVisitor是通过构造方法传入的。对于我们自己定义的那些Adapter来说,这是一个方便的父类。
例如SerialVersionUIDAdder的典型用法:
* ClassWriter cw = new ClassWriter(...);
* ClassVisitor sv = new SerialVersionUIDAdder(cw);
* ClassVisitor ca = new MyClassAdapter(sv);
* new ClassReader(orginalClass).accept(ca, false);
这段代码,首先生成一个ClassWriter,这是一个Visitor,用来得到最终的byte数组。另一个Visitor就是SerialVersionUIDAdder,它的参数是cw。第三个还是Visitor,MyClassAdapter。这三个Visitor形成了一个链。当ClassReader.accept()以ca作为参数,触发了解析过程的时候,会依次调用ca的各个visit方法。如果MyClassAdapter中override了某个visit方法,就会得到调用。
先略去MyClassAdapter,直接看SerialVersionUIDAdder的话,accept()会调用SerialVersionUIDAdder的各个visit方法,计算SerialVersionUID。SerialVersionUIDAdder本身并不改变class的逻辑,因此所有的visit方法都是delegate给cw,也就是ClassWriter。
在我自己的实现中,由于需要而稍微有点不同:
ClassReader cr = new ClassReader(classBytes);
ClassWriter cw = new ClassWriter(cr, 0);
MyEnhancer enhancer = new MyEnhancer(cw, fn);
ClassVisitor sv = new SerialVersionUIDAdder(enhancer);
cr.accept(sv, 0);
当ClassReader.accept()触发解析的时候,首先进行SerialVersionUID的计算,然后visit方法才会delegate给enhancer(这是通过ClassAdapter来实现的,因为SerialVersionUIDAdder是ClassAdapter的子类,构造方法传入的enhancer将作为父类所持有的成员变量)。这样SerialVersionUID就是原来的class所决定的值,而不是enhancer完成之后的值。这就是ASM的有趣的链式结构。
当然最后我没有用到SerialVersionUIDAdder







所以只要能让腿型好一点,体重不在乎了。昨天跑完了做了两下拉伸,以后要多做一点,昨天做的太少了。以后跑完看能不能去体操房拉伸一下,大厅里实在没地方,也不好意思,
他们总是诱惑我!