概述
在我们日常开发中,很多时候我们需要某些功能实现自动化的配置,如我们修改了数据库的字段,同时希望程序能自动跟着变更,正常情况我们是需要通过修改源代码,在涉及变更表的地方进行修改,这里修改的地方实际上就是Bean,因为我们都是面相对象开发,实际上我们修改完字段核心就是希望同步修改Bean,其他方面还有很多这种应用,具体根据实际业务具体分析。今天我们来说一说ASM。官方说明是ASM 是一个 Java 字节码操纵框架。它可以直接以二进制形式动态地生成 stub 类或其他代理类,或者在装载时动态地修改类,ASM 提供类似于 BCEL 和 SERP 之类的工具包的功能,但是被设计得更小巧、更快速,这使它适用于实时代码插装。
今天我们主要说说如何新建接口和Bean类,因为这两大类是我们常用的东西,其他的我们稍后在进行总结,在使用ASM之前,我们先说说ASM中的常用类。我们今天先说动态创建类,修改类下一个篇幅会详细介绍。我们创建接口其实比类简单很多,因为我们只需要实现方法的说明,不需要实现方法的实现,所以比较简单,我们先做一个讲解。
1,ASM动态创建接口
ClassWriter cw = new ClassWriter(0);
cw.visit(V1_6, ACC_PUBLIC + ACC_INTERFACE, "com/demo/asm/DemoInterface", null, "java/lang/Object", null);
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "NAME", "I", null, new Integer(-1)).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "AGE", "I", null, new Integer(0)).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "ADDRESS", "I", null, new Integer(1)).visitEnd();
cw.visitMethod(ACC_PUBLIC, "compareTo", "(Ljava/lang/Object;)I", null, null).visitEnd();
cw.visitMethod(ACC_PUBLIC, "getAge", "(I)I", null, null).visitEnd();
cw.visitEnd();
2,ASM动态创建类
// ----------------------------------------定义Bean类----------------------------------------------------------------
cw.visit(V1_6, ACC_PUBLIC, "com/demo/asm/DemoBean", null, "java/lang/Object", null);
cw.visitField(ACC_PUBLIC, "name", "Ljava/lang/String;", null, null).visitEnd();
MethodVisitor m_getName=cw.visitMethod(ACC_PUBLIC, "getName", "()Ljava/lang/String;", null, null);
m_getName.visitVarInsn(ALOAD, 0);
m_getName.visitFieldInsn(GETFIELD, "com/demo/asm/DemoBean", "name", "Ljava/lang/String;");
m_getName.visitInsn(ARETURN);
m_getName.visitMaxs(2, 1);
m_getName.visitEnd();
MethodVisitor m_setName=cw.visitMethod(ACC_PUBLIC, "setName", "(Ljava/lang/String;)V", null, null);
m_setName.visitVarInsn(ALOAD, 0);
m_setName.visitVarInsn(ALOAD, 1);
m_setName.visitFieldInsn(PUTFIELD, "com/demo/asm/DemoBean", "name", "Ljava/lang/String;");
m_setName.visitInsn(RETURN);
m_setName.visitMaxs(2,2);
m_setName.visitEnd();
cw.visitEnd();
// --------------------------------------------------------------------------------------------------------------
cw.visitField(ACC_PUBLIC, "age", "Ljava/util/Date;", null, null).visitEnd();
MethodVisitor m_getDate=cw.visitMethod(ACC_PUBLIC, "getDate", "()Ljava/util/Date;", null, null);
m_getDate.visitVarInsn(ALOAD, 0);
m_getDate.visitFieldInsn(GETFIELD, "com/demo/asm/DemoBean", "age", "Ljava/util/Date;");
m_getDate.visitInsn(ARETURN);
m_getDate.visitMaxs(2, 1);
m_getDate.visitEnd();
MethodVisitor m_setDate=cw.visitMethod(ACC_PUBLIC, "setDate", "(Ljava/util/Date;)V", null, null);
m_setDate.visitVarInsn(ALOAD, 0);
m_setDate.visitVarInsn(ALOAD, 1);
m_setDate.visitFieldInsn(PUTFIELD, "com/demo/asm/DemoBean", "age", "Ljava/util/Date;");
m_setDate.visitInsn(RETURN);
m_setDate.visitMaxs(2,2);
m_setDate.visitEnd();
cw.visitEnd();
byte[] code = cw.toByteArray();
// 将二进制流写到本地磁盘上
FileOutputStream fos = new FileOutputStream("src/com/demo/asm/DemoBean.class");
fos.write(code);
fos.close();
通过上面的两段代码其实大家实际运行一下就能看见效果,上面是创建了一个接口,下面创建了一个Bean类,但是里面有很多写法大家可能不是很明白,好的,现在开始整理代码中特殊的方法和类的说明。ASM使用visit的方式来进行字节码的操作。首先我们要学习一些JVM的知识,因为类加载过程了解的话学起来会很快,所以我们稍后也会专门写一遍可以实际执行的JVM类加载知识。
3.ASM类型,这里需要注意的地方是类型后面的分好不能省略。
Java type Type descriptor
boolean Z
char C
byte B
short S
int I
float F
long J
double D
Object Ljava/lang/Object;
int[] [I
String Ljava/lang/String;
Object[][] [[Ljava/lang/Object;
4.ASM方法描述
Method declaration in source file Method descriptor
void m(int i, float f) (IF)V
int m(Object o) (Ljava/lang/Object;)I
int[] m(int i, String s) (ILjava/lang/String;)[I
Object m(int[] i) ([I)Ljava/lang/Object;
这里详细说一下方法描述,方法描述我们括号里面是连续的无任何分隔符的类型声明,紧接着跟在括号的是返回值类型。
5.ASM创建类或接口
创建接口/类
cw.visit(V1_6, ACC_PUBLIC + ACC_INTERFACE, "com/demo/asm/DemoInterface", null, "java/lang/Object", null);
参数说明 1.V1_6这个是JDK的版本,这里使用的ASM5.2支持到JDK1.8,这里我们选择1.6.
2.ACC_PUBLIC + ACC_INTERFACE,这里注意,修饰符可以使用+号进行拼接,这里我们生命了一个public的interface,如果我们
不指出interface默认创建的就是类。这就是创建类和接口的区别。
3.要生产对象的全类名(包名+类名),这里也注意类名也要按照JAVA的命名规则进行编制。
4.泛型相关,这里我们没有使用就不在使用。
5.继承父类,我们所有的JAVA类默认都是继承Object类的,这里不在详细记录。
6.继承详细类说明,该参数是一个字符串数组。
创建Field
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I", null, new Integer(-1)).visitEnd();
参数说明 1.字段是public+final+static的。
2.字段名
3.字段类型
4.一般很少使用
5.字段的值。
创建Method
MethodVisitor m_getName=cw.visitMethod(ACC_PUBLIC, "getName", "()Ljava/lang/String;", null, null);
m_getName.visitVarInsn(ALOAD, 0);
m_getName.visitFieldInsn(GETFIELD, "org/dbzy/asm/DemoBean", "name", "Ljava/lang/String;");
m_getName.visitInsn(ARETURN);
m_getName.visitMaxs(2, 1);
m_getName.visitEnd();
MethodVisitor m_setName=cw.visitMethod(ACC_PUBLIC, "setName", "(Ljava/lang/String;)V", null, null);
m_setName.visitVarInsn(ALOAD, 0);
m_setName.visitVarInsn(ALOAD, 1);
m_setName.visitFieldInsn(PUTFIELD, "org/dbzy/asm/DemoBean", "name", "Ljava/lang/String;");
m_setName.visitInsn(RETURN);
m_setName.visitMaxs(2,2);
m_setName.visitEnd();
cw.visitEnd();
以上两个方法分别是创建的Bean的get,set方法。详细参数具体说明。
参数说明 1.方法访问修饰符public。
2.方法名称
3.方法参数及返回值类型
后两个参数很少使用。
4.visitVarInsn方法加载参数及变量。
5.visitFieldInsn访问动态Bean创建的字段。GETFIELD/PUTFIELD用于访问和修改字段的值。
6.visitInsn定义返回值。
将文件写入文件
byte[] code = cw.toByteArray();
FileOutputStream fos = new FileOutputStream("src/com/demo/asm/DemoBean.class");
fos.write(code);
fos.close();
以上就是今天的总结了,还有很多东西不是很详细,还需要在实践中不断完善,因为代码都是工具,而设计思想才是高层次的,所以我们需要不断在实践中总结经验,不断摸索来提高自己。
最后
以上就是无语小鸭子为你收集整理的ASM5.2动态创建接口及Bean类的全部内容,希望文章能够帮你解决ASM5.2动态创建接口及Bean类所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复