`
victorzhzh
  • 浏览: 202178 次
  • 来自: ...
社区版块
存档分类
最新评论

ASM系列之四:操作类属性

阅读更多

    在上一篇文章中,我们看到了ASM中的Core API中使用的是XXXVisitor操作类中的对应部分。本文将展示如何使用ASM中的Core API对类的属性的操作。

首先,我们定义一个原类Person,如下:

public class Person {
	public String name = "zhangzhuo";
	public String address = "xxxxx" ;
}

 这里,我们将属性定义为public类型,目的是为了我们使用反射去调用这个属性,接下来我们要为这个类添加一个int类型的属性,名字叫age。

    第一个问题,ASM的Core API允许我们在那些方法中来添加属性?

    在ASM的Core API中你要为类添加属性就必须要自己去实现ClassVisitor这个接口,这个接口中的visitInnerClass、visitField、visitMethod和visitEnd方法允许我们进行添加一个类属性操作,其余的方法是不允许的。这里我们依然使用Core API中的ClassAdapter类,我们继承这个类,定义一个去添加属性的类,ClassAdapter实现了ClassVisitor。

    第二个问题,我们要在这些方法中写什么样的代码才能添加一个属性?

    在使用ASM的Core API添加一个属性时只需要调用一句语句就可以,如下:

classVisitor.visitField(Opcodes.ACC_PUBLIC, "age", Type.getDescriptor(int.class),
				null, null);

 第一个参数指定的是这个属性的操作权限,第二个参数是属性名,第三个参数是类型描述,第四个参数是泛型类型,第五个参数是初始化的值,这里需要注意一下的是第五个参数,这个参数只有属性为static时才有效,也就是数只有为static时,这个值才真正会赋值给我们添加的属性上,对于非static属性,它将被忽略。

好了,让我们看一段代码,在visitEnd去添加一个名字为age的属性:

public class Transform extends ClassAdapter {

	public Transform(ClassVisitor cv) {
		super(cv);
	}

	@Override
	public void visitEnd() {
		cv.visitField(Opcodes.ACC_PUBLIC, "age", Type.getDescriptor(int.class),
				null, null);
	}

}

 非常简单吧,只要一句话就可以添加一个属性到我们的类中,看一下我们的测试类:

public class TransformTest {
	@Test
	public void addAge() throws Exception {
		ClassReader classReader = new ClassReader(
				"org.victorzhzh.core.field.Person");
		ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
		ClassAdapter classAdapter = new Transform(classWriter);

		classReader.accept(classAdapter, ClassReader.SKIP_DEBUG);

		byte[] classFile = classWriter.toByteArray();

		GeneratorClassLoader classLoader = new GeneratorClassLoader();
		Class clazz = classLoader.defineClassFromClassFile(
				"org.victorzhzh.core.field.Person", classFile);
		Object obj = clazz.newInstance();

		System.out.println(clazz.getDeclaredField("name").get(obj));//----(1)
		System.out.println(clazz.getDeclaredField("age").get(obj));//----(2)
	}
}

 在这里,如果我们的age没有被添加进去那么(2)这个地方将会报错,看一下结果:

zhangzhuo
0

 int类型在没有被初始化时,默认值为0,而第二行输出0,说明我们添加了一个属性age

 

接下来,我们在visitField方法中在次添加age属性,如下:

public class Transform extends ClassAdapter {

	public Transform(ClassVisitor cv) {
		super(cv);
	}

	@Override
	public FieldVisitor visitField(int access, String name, String desc,
			String signature, Object value) {
		cv.visitField(Opcodes.ACC_PUBLIC, "age", Type.getDescriptor(int.class),
				null, null);
		return super.visitField(access, name, desc, signature, value);
	}

}

 这时,我们再次运行测试类,结果如下:

java.lang.ClassFormatError: Duplicate field name&signature in class file org/victorzhzh/core/field/Person
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:466)
	at org.victorzhzh.common.GeneratorClassLoader.defineClassFromClassFile(GeneratorClassLoader.java:14)
	at org.victorzhzh.core.field.TransformTest.addAge(TransformTest.java:22)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

 很奇怪,怎么会属性名重复,我们看一下原类,

public String name = "zhangzhuo";
public String address = "xxxxx" ;

 没有重复的名字,而我们添加的是age也不重复,为什么会报重复属性名错误呢?

原因是,在我们的Transform类中的visitField方法,这个方法会在每次属性被访问时调用,而ASM在对这个类操作时会遍历到每个属性,也就是说有一个属性就会调用一次visitField方法,有两个属性就会调用两次visitField方法,所以当我们原类中有两个属性时visitField方法被调用了两次,因此创建了两个同名的age属性。

 

从这个例子中我们可以将visitInnerClass、visitField、visitMethod和visitEnd这些方法分成两组,一组是visitInnerClass、visitField、visitMethod,这些方法有可能会被多次调用,因此在这些方法中创建属性时要注意会重复创建;另一组是visitEnd,这个方法只有在最后才会被调用且只调用一次,所以在这个方法中添加属性是唯一的,因此一般添加属性选择在这个方法里编码。

    当然这里只给出了如何创建一个属性,其实修改,删除也都一样,根据上述知识大家可以参考ASM的源码即可掌握修改删除等操作。

 

附GeneratorClassLoader类代码

public class GeneratorClassLoader extends ClassLoader {

	@SuppressWarnings("rawtypes")
	public Class defineClassFromClassFile(String className, byte[] classFile)
			throws ClassFormatError {
		return defineClass(className, classFile, 0, classFile.length);
	}
}
 
分享到:
评论

相关推荐

    asm操作指南(中文)

    Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至...

    初学asm的简单例子

    【例2.8】存储器与寄存器间数据传送。...第四步:运行可执行文件EXAM?EXE A>EXAM 运行结果如下: I am a student! 若未得到预期的结果,可检查EXAM?ASM文件内容,修改错误,再次汇编、连接、运行,直到满意为止。

    asm-tree-3.3.1.jar

    例如,可以使用ASM Tree API遍历一个类的整个字节码结构,查找并修改特定的指令或属性,或者在运行时动态生成新的类和方法。此外,由于ASM Tree API具有较高的抽象层次,因此它适用于各种类型的Java字节码操作,无论...

    自己动手写操作系统(含源代码).part2

    第三,实践类的操作系统书籍还是太少了,以至于你要想看看别人是怎么做的,除了读以《操作系统:设计与实现》为代表的极少数书籍之外,就是一头扎进源代码中,而结果有时相当令人气馁。我自己也气馁过,所以我在第二...

    构建最高可用Oracle数据库系统 Oracle 11gR2 RAC管理、维护与性能优化

    4.5 ASM操作 4.5.1 RDBMS操作ASM文件 4.5.2 ASM文件的分配 4.5.3 ASM区间读写特性 4.5.4 ASM同步技术 4.5.5 ASM实例恢复和Crash恢复 4.5.6 ASM磁盘组操作 4.6 ACFS集群文件系统 4.6.1 ACFS概述 4.6.2 ADVM...

    自己动手写操作系统(含源代码).part1

    第三,实践类的操作系统书籍还是太少了,以至于你要想看看别人是怎么做的,除了读以《操作系统:设计与实现》为代表的极少数书籍之外,就是一头扎进源代码中,而结果有时相当令人气馁。我自己也气馁过,所以我在第二...

    精通DirectX.3D图形与动画程序设计.pdf

    第三部分介绍了direct3d gpu编程(即可编程流水线),包括hlsl渲染语言、hlsl顶点渲染、hlsl像素渲染、effect及其高级应用、asm顶点渲染、asm像素渲染。第四部分介绍了三维图形程序设计领域目前流行的许多实用技术,...

    -C++参考大全(第四版) (2010 年度畅销榜

    23.8 利用关键字asm 23.9 连接说明 23.10 基于数组的I/O 23.11 C与C++的区别 第24章 标准模板库 24.1 STL概述 24.2 容器类 24.3 一般的操作原理 24.4 vector容器 24.5 list容器 24.6 map容器 24.7 算法 24.8 使用...

    Hibernate中文API

    你可以看到这个类对属性的存取方法(getter and setter method)使用了标准JavaBean命名约定,同时把类属性(field)的访问级别设成私有的(private)。这是推荐的设计,但并不是必须的。Hibernate也可以直接访问...

    22春“计算机科学与技术”专业《嵌入式系统与结构》在线作业一答案参考9.docx

    类的静态属性和全局变量的概念完全一样,只是表达形式不同 B.类的成员至少有一个属性和一个方法 C.类是对象的实例化 D.Java的类分为两大部分系统定义的类和用户自定义的类 参考答案:D 22春"计算机科学与技术"专业...

    网管教程 从入门到精通软件篇.txt

    ASM:汇编语言源文件,Pro/E装配文件 ASP:动态网页文件;ProComm Plus安装与连接脚本文件;Astound介绍文件 AST:Astound多媒体文件;ClarisWorks“助手”文件 Axx:ARJ压缩文件的分包序号文件,用于将一个大...

    易语言骚操作之动态数据类型-易语言

    变量的束缚,把它变成孤儿指针我从落叶的通用型哈希表那边学来的:哈希表类_汇编版(HashMap_ASM) 支持自定义数据值 核心思路如下图: 当我们在代码中引用 局_自定义数据 的时候,易语言才会初始化这个变量,如果没有...

    c#学习笔记.txt

    Sun就从来没有将它的Java交给过ECMA,以至于正当Microsoft尽力在Visual J++基础上拓展Java功能,并使之与Windows操作系统紧密结合在一起的时候,Sun公司对Microsoft提出了法律诉讼,控告Microsoft违反了许可证协议中...

    OCPOCA认证考试指南全册:Oracle Database 11g(1Z0-051,1Z0-052,1Z0-053)--详细书签版(第1/2部分)

    1.1.1 Oracle服务器系列 4 1.1.2 Oracle开发工具 7 1.1.3 Oracle应用程序 9 1.2 预备知识 9 1.2.1 Oracle概念 10 1.2.2 SQL概念 10 1.2.3 操作系统概念 11 1.3 单实例体系结构 11 1.3.1 单实例数据库体系...

    OCPOCA认证考试指南全册:Oracle Database 11g(1Z0-051,1Z0-052,1Z0-053)--详细书签版(第2/2部分)

    1.1.1 Oracle服务器系列 4 1.1.2 Oracle开发工具 7 1.1.3 Oracle应用程序 9 1.2 预备知识 9 1.2.1 Oracle概念 10 1.2.2 SQL概念 10 1.2.3 操作系统概念 11 1.3 单实例体系结构 11 1.3.1 单实例数据库体系...

    使用类似 SQL 的查询查找文件

    安装来自源头的最新版本使用 Cargo及其依赖项安装Rust以构建二进制文件运行cargo install fselect拱形LinuxAUR 包,感谢@asm0dey操作系统fselect innixpkgs ,感谢@filalex77其他 Linux使用 musl 进行静态构建。...

Global site tag (gtag.js) - Google Analytics