Classworking 工具箱: 注释(Annotation)与 ASM
2010-03-18 00:00:00 来源:WEB开发网清单 5 的代码保存了访问字段时的字段信息,因为如果该字段有注释呈现的话,以后将会需要该信息。当访问注释时,该代码审查它是否是 ToString 注释,如果是,设置一个标志,说明当前字段应该被包含在用于生成 toString() 方法的列表中。当访问一个注释名值对时,该代码审查由 ToString 注释定义的两个名称,当找到时,保存每个名称的值。这些名称的真正默认值(与在注释定义中使用的默认值相对)是在字段的 visitor 方法中设置的,所以任意由用户指定的值都将重写这些默认值。
ASM 首先访问字段,接着访问注释和注释值。因为在处理字段的注释时,没有特定的方法可以调用,所以当处理一个新字段和当需要字段的完成列表时,我会调用一个 finishField() 方法。getFields() 方法向调用者提供字段的完成列表,以由注释值所确定的顺序排列。
转换类
清单 6 展示了实现代码的最后部分,它实际上向类添加了 toString() 方法。该代码与 上个月的文章 中使用 ASM 构造一个类的代码类似,但是需要另外构造以修改一个已有的类。这里,ASM 使用的 visitor 方法增加了复杂性 —— 要修改一个已有的类,需要访问所有的当前类目录,并把它传递给类编写者。org.objectweb.asm.ClassAdapter 是针对此目的的一个方便的基类。它实现了对提供的类编写者实例的传递处理,使您可以只重写需要特殊处理的方法。
清单 6. 添加 toString() 方法
package com.sosnoski.asm;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* Visitor to add <code>toString</code> method to a class.
*/
public class ToStringGenerator extends ClassAdapter
{
private final ClassWriter m_writer;
private final String m_internalName;
private final FieldInfo[] m_fields;
public ToStringGenerator(ClassWriter cw, String iname, FieldInfo[] props) {
super(cw);
m_writer = cw;
m_internalName = iname;
m_fields = props;
}
// called at end of class
public void visitEnd() {
// set up to build the toString() method
MethodVisitor mv = m_writer.visitMethod(Opcodes.ACC_PUBLIC,
"toString", "()Ljava/lang/String;", null, null);
mv.visitCode();
// create and initialize StringBuffer instance
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuffer");
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuffer",
"<init>", "()V");
// start text with class name
String name = m_internalName;
int split = name.lastIndexOf('/');
if (split >= 0) {
name = name.substring(split+1);
}
mv.visitLdcInsn(name + ":");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuffer",
"append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
// loop through all field values to be included
boolean newline = false;
for (int i = 0; i < m_fields.length; i++) {
// check type of field (objects other than Strings need conversion)
FieldInfo prop = m_fields[i];
Type type = prop.getType();
boolean isobj = type.getSort() == Type.OBJECT &&
!type.getClassName().equals("java.lang.String");
// format lead text, with newline for object or after object
String lead = (isobj || newline) ? "\n " : " ";
if (prop.getText().length() > 0) {
lead += prop.getText() + "=";
}
mv.visitLdcInsn(lead);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuffer", "append",
"(Ljava/lang/String;)Ljava/lang/StringBuffer;");
// load the actual field value and append
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, m_internalName,
prop.getField(), type.getDescriptor());
if (isobj) {
// convert objects by calling toString() method
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
type.getInternalName(), "toString",
"()Ljava/lang/String;");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuffer", "append",
"(Ljava/lang/String;)Ljava/lang/StringBuffer;");
} else {
// append other types directly to StringBuffer
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuffer", "append", "(" +
type.getDescriptor() + ")Ljava/lang/StringBuffer;");
}
newline = isobj;
}
// finish the method by returning accumulated text
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuffer",
"toString", "()Ljava/lang/String;");
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(3, 1);
mv.visitEnd();
super.visitEnd();
}
}
Tags:Classworking 工具箱 注释
编辑录入:爽爽 [复制链接] [打 印]赞助商链接