在开发中一般情况下我们写好的代码然后编译成class文件并运行属于静态的class文件生成,那是不是class文件就只有静态生成一种啊,其实不然,在jdk的动态代理应用Proxy类就是已经使用了动态生成一个class文件来实现代理功能的,只是这一部分我们都看不到,而asm是一个专门的字节码动态生成的一个框架,其架构使用的是生产、消费和过滤模式对应的接口和类分别是ClassReader读取一个Class文件属于生产模式,classWriter是生产一个class文件属于消费模式,ClassAdapter是对ClassWriter的过滤,做修改功能的,classReader接受一个ClassWriter并在方法中调用ClassWriter的所有的方法来实现生产并消费。当我们修改一个类的时候就必须继承ClassAdapter利用ClassAdapter中包含的ClassWriter对象对class文件进行修改。如果只是动态生成一个class就直接使用classWriter和CodeWriter就可以了。运用asm进行动态生成和修改一个class文静必须了解class文件的结构和一些指令。但是CGLib(Code Generator Library代码生成库)帮我们做了封装和扩展让我们更容易去实现这个功能,不用了解class文件的结构也可以做到。其中CGLib的类的动态代理就是利用asm字节码动态生成而实现的,在这里说一下CGLib只是对asm的封装和扩展,CGLib的对class文件及一个类的操作是基于asm的欢聚换说asm是CGLib的底层,关于asm可以参考asm使用手册。网上是可以下载的,有兴趣的话可以研究一下。下面给出一些实际操作的小例子:
public void generator()
{//自动生成一个class文件
ClassWriter cw=new ClassWriter(0);
cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT, "com/test/bean/Base", null,"com/test/bean/Test", new String[]{"com/test/bean/IDAO"});
cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC+Opcodes.ACC_FINAL, "name", "Ljava/lang/String;",null,"accp" ).visitEnd();
cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC+Opcodes.ACC_FINAL, "age", "I",null, new Integer(20)).visitEnd();
cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"hello", "()V", null,null).visitEnd();
cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"add", "(Ljava/lang/Object;)V", null,null).visitEnd();
cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"update","(Ljava/lang/Object;)V",null,null).visitEnd();
cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"delete","(Ljava/lang/Object;)V", null,null).visitEnd();
cw.visitEnd();
byte[] b=cw.toByteArray();//构建好一个class文件
ClassReader cr=new ClassReader(b);
ClassWriter cw2=new ClassWriter(0);
//给class文件添加一个方法
cr.accept(new AddMethodAdapter(cw2,Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"get","(Ljava/lang/String;)Ljava/lang/Object;"), 0);
//从class文件中移除一个方法
//cr.accept(new RemoveMethodAdapter(cw2,"delete","(Ljava/lang/Object;)V"), false);
b=cw2.toByteArray();//生成新的class文件
try {
FileOutputStream out=new FileOutputStream(new File("D:Base.class"));
out.write(b);
out.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 移除方法过滤器
* @author liurm
*
*/
public class RemoveMethodAdapter extends ClassAdapter {
private String name;//方法名称
private String desc;//方法描述
public RemoveMethodAdapter(ClassVisitor classvisitor,String name,String desc) {
super(classvisitor);
// TODO Auto-generated constructor stub
this.name=name;
this.desc=desc;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
// TODO Auto-generated method stub
if(name.equals(this.name)&&desc.equals(this.desc))
{
return null;//移除方法
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
/*
* 添加方法过滤器
* */
public class AddMethodAdapter extends ClassAdapter{
private int acc;//访问修饰符
private String name;//方法名称
private String desc;//方法描述
private boolean isMethodPersent;//是否已经添加
public AddMethodAdapter(ClassVisitor classvisitor,int acc,String name,String desc) {
super(classvisitor);
// TODO Auto-generated constructor stub
this.acc=acc;
this.name=name;
this.desc=desc;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
// TODO Auto-generated method stub
if(name.equals(this.name)&&desc.equals(this.desc))
{//方法已经添加
isMethodPersent=true;
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
public void visitEnd() {
// TODO Auto-generated method stub
if(!isMethodPersent)
{//未添加是才添加
super.cv.visitMethod(acc, name, desc, null, null);
}
super.visitEnd();
}
}
class PrintClass implements org.objectweb.asm.ClassVisitor{
public void visitAttribute(Attribute attribute) {
// TODO Auto-generated method stub
}
public void visitEnd() {
// TODO Auto-generated method stub
System.out.println("}");
}
public void visitInnerClass(String name, String outername, String innername, int access) {
// TODO Auto-generated method stub
}
public void visit(int version, int access, String name, String signutre,
String supername, String[] interfacename) {
// TODO Auto-generated method stub
System.out.println(name+" extends "+supername+"{");
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// TODO Auto-generated method stub
return null;
}
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
// TODO Auto-generated method stub
System.out.println(desc+" "+name);
return null;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
// TODO Auto-generated method stub
System.out.println(name+desc);
return null;
}
public void visitOuterClass(String owner, String name, String desc) {
// TODO Auto-generated method stub
}
public void visitSource(String arg0, String arg1) {
// TODO Auto-generated method stub
}
}
生成Get,Set方法,一个普通的java类
ClassWriter cw=new ClassWriter(0);//手动计算内存
cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC,"com/test/bean/Student",null,"java/lang/Object",null);//声明一个类
cw.visitField(Opcodes.ACC_PRIVATE, "age", "I", null,new Integer(20)).visitEnd();//定义age字段
cw.visitField(Opcodes.ACC_PRIVATE, "name", "Ljava/lang/String;",null,"accp").visitEnd();//定义name字段
//构造函数
MethodVisitor mv4=cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
mv4.visitCode();
mv4.visitVarInsn(Opcodes.ALOAD, 0);
mv4.visitMethodInsn(Opcodes.INVOKESPECIAL, "com/test/bean/Student", "<init>", "()V");
mv4.visitInsn(Opcodes.RETURN);
mv4.visitMaxs(1, 1);
mv4.visitEnd();
//构建getAge方法
MethodVisitor mv=cw.visitMethod(Opcodes.ACC_PUBLIC, "getAge", "()I", null,null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, "com/test/bean/Student", "age", "I");
mv.visitInsn(Opcodes.IRETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
//构建setAge方法
MethodVisitor mv1=cw.visitMethod(Opcodes.ACC_PUBLIC, "setAge", "(I)V", null, null);
mv1.visitCode();
mv1.visitVarInsn(Opcodes.ALOAD, 0);
mv1.visitVarInsn(Opcodes.ALOAD, 1);
mv1.visitFieldInsn(Opcodes.PUTFIELD, "com/test/bean/Student", "age", "I");
mv1.visitInsn(Opcodes.RETURN);
mv1.visitMaxs(2, 2);
mv1.visitEnd();
//构建getName方法
MethodVisitor mv2=cw.visitMethod(Opcodes.ACC_PUBLIC, "getName", "()Ljava/lang/String;", null,null);
mv2.visitCode();
mv2.visitVarInsn(Opcodes.ALOAD, 0);
mv2.visitFieldInsn(Opcodes.GETFIELD, "com/test/bean/Student", "name", "Ljava/lang/String;");
mv2.visitInsn(Opcodes.ARETURN);
mv2.visitMaxs(1, 1);
mv2.visitEnd();
//构建setName方法
MethodVisitor mv3=cw.visitMethod(Opcodes.ACC_PUBLIC, "setName", "(Ljava/lang/String;)V", null, null);
mv3.visitCode();
mv3.visitVarInsn(Opcodes.ALOAD, 0);
mv3.visitVarInsn(Opcodes.ALOAD, 1);
mv3.visitFieldInsn(Opcodes.PUTFIELD, "com/test/bean/Student", "name", "Ljava/lang/String;");
mv3.visitInsn(Opcodes.RETURN);
mv3.visitMaxs(2, 2);
mv3.visitEnd();
//sayhello方法
MethodVisitor mv5=cw.visitMethod(Opcodes.ACC_PUBLIC, "sayhello", "(Ljava/lang/String;)Ljava/lang/String;", null, null);
mv5.visitCode();
mv5.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv5.visitInsn(Opcodes.DUP);
mv5.visitLdcInsn("Hello:");
mv5.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv5.visitVarInsn(Opcodes.ALOAD, 1);
mv5.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
mv5.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv5.visitInsn(Opcodes.ARETURN);
mv5.visitMaxs(3, 2);
mv5.visitEnd();
//静态块
MethodVisitor mv6=cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>","()V", null,null);
mv6.visitCode();
//System.out.println("Hello World!");
mv6.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv6.visitLdcInsn("Hello World!");
mv6.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
//创建数组int [] a=new int[5];
//mv6.visitInsn(Opcodes.ICONST_5);
//int [] a=new int[6];
mv6.visitLdcInsn(new Integer(6));
//mv6.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT);
mv6.visitVarInsn(Opcodes.NEWARRAY, Opcodes.T_INT);
mv6.visitVarInsn(Opcodes.ASTORE, 4);
mv6.visitInsn(Opcodes.RETURN);
mv6.visitMaxs(4,0);
mv6.visitEnd();
cw.visitEnd();
byte[] b=cw.toByteArray();//可以装载到JVM中了