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

ASM系列之三:ASM中的访问者模式

阅读更多

    在ASM的Core API中使用的是访问者模式来实现对类的操作,主要包含如下类:

一、ClassVisitor接口:

    在这个接口中主要提供了和类结构同名的一些方法,这些方法可以对相应的类结构进行操作。如下:

public interface ClassVisitor {
    void visit(int version,int access,String name,String signature,String superName,String[] interfaces);
    void visitSource(String source, String debug);
    void visitOuterClass(String owner, String name, String desc);
    AnnotationVisitor visitAnnotation(String desc, boolean visible);
    void visitAttribute(Attribute attr);
    void visitInnerClass(String name,String outerName,String innerName,int access);
    FieldVisitor visitField(int access,String name,String desc,String signature,Object value);
    MethodVisitor visitMethod(int access,String name,String desc,String signature,String[] exceptions);
    void visitEnd();
}

 这里定义的方法调用是有顺序的,在ClassVisitor中定义了调用的顺序和每个方法在可以出现的次数,如下:

visit [ visitSource ] [ visitOuterClass ] ( visitAnnotation | visitAttribute )* (visitInnerClass | visitField | visitMethod )* visitEnd。

二、ClassReader类:

    这个类会提供你要转变的类的字节数组,它的accept方法,接受一个具体的ClassVisitor,并调用实现中具体的visit,

visitSource, visitOuterClass, visitAnnotation, visitAttribute, visitInnerClass, visitField, visitMethod和 visitEnd方法。

三、ClassWriter类:

    这个类是ClassVisitor的一个实现类,这个类中的toByteArray方法会将最终修改的字节码以byte数组形式返回,在这个类的构造时可以指定让系统自动为我们计算栈和本地变量的大小(COMPUTE_MAXS),也可以指定系统自动为我们计算栈帧的大小(COMPUTE_FRAMES)。

四、ClassAdapter类:

   这个类也是ClassVisitor的一个实现类,这个类可以看成是一个事件过滤器,在这个类里,它对ClassVisitor的实现都是委派给一个具体的ClassVisitor实现类,即调用那个实现类实现的方法。

五、AnnotationVisitor接口:

   这个接口中定义了和Annotation结构想对应的方法,这些方法可以操作Annotation中的定义,如下:

public interface AnnotationVisitor {
    void visit(String name, Object value);
    void visitEnum(String name, String desc, String value);
    AnnotationVisitor visitAnnotation(String name, String desc);
    AnnotationVisitor visitArray(String name);
    void visitEnd();
}

 调用顺序如下:

(visit | visitEnum | visitAnnotation | visitArray)* visitEnd

六、FieldVisitor接口:

   这个接口定义了和属性结构相对应的方法,这些方法可以操作属性,如下:

public interface FieldVisitor {
    AnnotationVisitor visitAnnotation(String desc, boolean visible);
    void visitAttribute(Attribute attr);
    void visitEnd();
}

 调用顺序:

( visitAnnotation | visitAttribute )* visitEnd .

七、MethodVisitor接口:

    这个接口定义了和方法结构相对应的方法,这些方法可以去操作源方法,具体的可以查看一下源码。

八、操作流程:

   一般情况下,我们需要操作一个类时,首先是获得其二进制的字节码,即用ClassReader来读取一个类,然后需要一个能将二进制字节码写回的类,即用ClassWriter类,最后就是一个事件过滤器,即ClassAdapter。事件过滤器中的某些方法可以产生一个新的XXXVisitor对象,当我们需要修改对应的内容时只要实现自己的XXXVisitor并返回就可以了。

九、例子:

    在这个例子中,我们将对Person类的sayName方法做出一些修改,源类:

public class Person {
	private String name;

	public void sayName() {
		System.out.println(name);
	}
}

  如果我们定义一个Person类然后调用其sayName()方法将会得到的是一个null,行成的二进制字节码如下:

public void sayName();
  Code:
   Stack=2, Locals=1, Args_size=1
   0:	getstatic	#17; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:	aload_0
   4:	getfield	#23; //Field name:Ljava/lang/String;
   7:	invokevirtual	#25; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   10:	return



}

 我们修改一下这个方法,让它输出"zhangzhuo",代码如下:

public class GenerateNewPerson {
	public static void main(String[] args) throws Exception {
		// 使用全限定名,创建一个ClassReader对象
		ClassReader classReader = new ClassReader(
				"org.victorzhzh.core.ic.Person");
		// 构建一个ClassWriter对象,并设置让系统自动计算栈和本地变量大小
		ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);

		ClassAdapter classAdapter = new GeneralClassAdapter(classWriter);

		classReader.accept(classAdapter, ClassReader.SKIP_DEBUG);

		byte[] classFile = classWriter.toByteArray();

		// 将这个类输出到原先的类文件目录下,这是原先的类文件已经被修改
		File file = new File(
				"target/classes/org/victorzhzh/core/ic/Person.class");
		FileOutputStream stream = new FileOutputStream(file);
		stream.write(classFile);
		stream.close();
	}
}

public class GeneralClassAdapter extends ClassAdapter {

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

	@Override
	public MethodVisitor visitMethod(int access, String name, String desc,
			String signature, String[] exceptions) {
		MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
				exceptions);
		// 当是sayName方法是做对应的修改
		if (name.equals("sayName")) {
			MethodVisitor newMv = new SayNameMethodAdapter(mv);
			return newMv;
		} else {
			return mv;
		}
	}

	// 定义一个自己的方法访问类
	class SayNameMethodAdapter extends MethodAdapter {
		public SayNameMethodAdapter(MethodVisitor mv) {
			super(mv);
		}

		// 在源方法前去修改方法内容,这部分的修改将加载源方法的字节码之前
		@Override
		public void visitCode() {
			// 记载隐含的this对象,这是每个JAVA方法都有的
			mv.visitVarInsn(Opcodes.ALOAD, 0);
			// 从常量池中加载“zhangzhuo”字符到栈顶
			mv.visitLdcInsn("zhangzhuo");
			// 将栈顶的"zhangzhuo"赋值给name属性
			mv.visitFieldInsn(Opcodes.PUTFIELD,
					Type.getInternalName(Person.class), "name",
					Type.getDescriptor(String.class));
		}

	}

}

 这时,我们在查看一下Person的字节码:

public void sayName();
  Code:
   Stack=2, Locals=1, Args_size=1
   0:	aload_0
   1:	ldc	#13; //String zhangzhuo
   3:	putfield	#15; //Field name:Ljava/lang/String;
=============以上是我们新增加的内容================================
   6:	getstatic	#21; //Field java/lang/System.out:Ljava/io/PrintStream;
   9:	aload_0
   10:	getfield	#15; //Field name:Ljava/lang/String;
   13:	invokevirtual	#27; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   16:	return

}

 再次调用Person对象,输出结果为:zhangzhuo

 

分享到:
评论
2 楼 jianglinjun 2016-11-22  
public void sayName();
  Code:
   Stack=2, Locals=1, Args_size=1
   0: aload_0
   1: ldc #13; //String zhangzhuo
   3: putfield #15; //Field name:Ljava/lang/String;
=============以上是我们新增加的内容================================
   6: getstatic #21; //Field java/lang/System.out:Ljava/io/PrintStream;
   9: aload_0
   10: getfield #15; //Field name:Ljava/lang/String;
   13: invokevirtual #27; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   16: return

}

请问这些内容在哪里查看...?????
1 楼 kakarottoz 2015-01-12  
写的太好了,赞!

相关推荐

    asm 6.0 工具集

    ASM通过访问者模式设计,使得在字节码级别进行操作变得简洁高效。ASM支持JVM规范的所有版本,包括Java 8及更高版本的特性,如lambda表达式和方法引用来实现反射和AOP(面向切面编程)。 **ASM-Util**是ASM的实用...

    asm-all-3.3

    6. **ASM Example**:这部分提供了示例代码,展示如何使用ASM来实现各种功能,如动态代理、代码修改等,对于初学者理解和学习ASM框架十分有帮助。 在ASM 3.3的压缩包中,`asm-3.3`很可能包含了上述所有模块的jar...

    [字节码系列]ObjectWeb ASM构建Method Monitor

    2. **创建访问者**:ASM的核心是访问者模式。我们需要创建一个自定义的`ClassVisitor`子类,这个访问者会处理每个方法的开始和结束,并决定是否需要插入监控代码。 3. **插入监控代码**:在访问者中,我们重写`...

    asm-2.2.3.jar,asm-commons-2.2.3.jar,asm-util-2.2.3.jar

    不过,ASM提供了高级访问者模式,降低了使用难度,使得即使不精通字节码的开发者也能较容易地使用它。 总结起来,ASM是一个强大而灵活的字节码框架,它在许多Java工具和库中扮演着重要角色。asm-2.2.3.jar、asm-...

    asm官方代码文档

    访问者模式则利用设计模式中的访问者模式,使得对类结构的遍历和修改更为灵活。 2. **类和字段访问器**:ASM提供了ClassWriter和FieldVisitor接口,用于创建和修改类及字段信息。这些访问器允许我们设置访问权限、...

    asm 3.3.1 jar

    2. **灵活性**:通过访问者模式,ASM允许开发者在运行时动态创建和修改类,实现代码注入和类的自定义增强。 3. **全面支持Java字节码**:ASM可以处理Java 5及以后版本的特性,包括泛型、注解和枚举等。 **ASM的工作...

    学习asm汇编的代码

    在这个“学习asm汇编的代码”压缩包中,包含了一系列的ASM源文件,它们可能是为了教学或实践目的编写的示例代码。接下来,我们将详细探讨这些文件可能涉及的ASM汇编语言相关知识点。 1. **CIH13.asm、CIH14.asm、...

    asm-tree(3.0 /3.3)

    ASM访问者模式是一种设计模式,它定义了一种一对多的依赖关系,让多个访问者对象可以访问同一层次结构的对象,而不需要暴露这个层次结构的内部细节。通过这种方式,`asm-tree`使得我们可以方便地遍历和修改字节码,...

    asm的jar包

    这个库通常会提供一系列类,如`ClassReader`用于读取类文件,`ClassWriter`用于生成类文件,以及`ClassAdapter`作为适配器模式的基础,允许自定义处理字节码的逻辑。还有其他类如`MethodVisitor`和`FieldVisitor`,...

    ASM中文帮助文档

    - **代码分析**:在性能调优、代码安全审计或白盒测试中,ASM能帮助解析类结构和方法体。 - **代码优化**:ASM可以用来对字节码进行优化,例如消除冗余的计算或减少内存分配。 - **AOP(面向切面编程)**:ASM...

    oracle不使用oracleasm的包配置ASM磁盘配置方法

    ### Oracle 不使用 OracleASM 的包配置 ASM 磁盘配置方法 #### 概述 在 Oracle 数据库系统中,自动存储管理(ASM)是用于管理数据库文件的一种高性能、高可用性的解决方案。通常情况下,ASM 依赖于 Oracle 提供的 ...

    ASM 字节码修改工具中文帮助手册

    - **介绍**: ASM 在新版本中会尽可能保持后向兼容性,以避免现有代码因版本升级而失效。 - **指引**: - 提供了关于如何确保后向兼容性的指导原则。 - 包括基本规则、继承规则和其他包装规则。 #### 9. 附录 - **...

    德国ASM位移传感器样本

    9. 公司信息:文件中提到了ASMGmbH以及***,前者是ASM公司的缩写,后者很可能是公司的网站地址,用户可以通过该网址访问ASM公司的官方信息,获取更详细的传感器数据、技术规格和应用信息。 10. 产品型号的多样性:...

    asm-7.1.zip

    - 熟悉ASM提供的API,尤其是访问者模式的设计,它是ASM的核心编程模型。 - 谨慎操作字节码,因为错误的修改可能导致程序崩溃或安全问题。 - 利用asm-util中的工具,如ASMifier,可以帮助理解和调试生成的字节码。 ...

    ASM4.2 DEMO

    ASM提供了一系列的基础访问器,如ClassVisitor、MethodVisitor和FieldVisitor,它们遵循了一种访问者模式,使得我们可以方便地遍历和操作类结构。 在ASM 4.2中,一个主要的改进是对Java 8的支持,包括对lambda...

    asm-transformations.pdf

    通过采用访问者设计模式,ASM能够在处理字节码时避免大量对象的创建,从而减少时间和内存开销。这种模式使得ASM能够快速执行转换操作,并保持极低的内存使用量,这是其他框架难以匹敌的。 #### 三、字节码转换在AOP...

    通过asm把手机屏幕投影到电脑

    4. 在命令行中输入“java -jar asm.jar”,这行命令会启动ASM,此时如果安卓设备已经连接到电脑,并且开启了USB调试模式,ASM应该能够检测到设备并开始进行屏幕投影。 关于ASM的使用,除了基本的屏幕投影,还有一些...

    创建oracle10g asm数据库step by step

    ASM的条带化和镜像功能,不仅简化了DBA的操作,还有效提高了数据访问速度和数据冗余,是Oracle 10g数据库架构设计中的一大亮点。 综上所述,利用VMware构建的虚拟环境,配合详细的步骤指导,即便是初学者也能顺利...

    Android-Android无痕埋点框架使用ASM插桩实现

    ASM的核心在于它的事件驱动模型,通过访问者模式,开发者可以在字节码的解析过程中添加自定义逻辑。 在Android无痕埋点中,ASM插桩的原理是利用Java字节码注入技术,在运行时动态插入埋点代码。当用户执行特定操作...

    ASM 中文教程包含一个拼图游戏

    总的来说,ASM中文教程结合拼图游戏,提供了一个有趣且实用的学习路径,帮助学习者在实践中深化对ASM的理解,同时也培养了他们的编程思维和问题解决技巧。无论是对计算机科学的初学者还是希望深入研究底层机制的...

Global site tag (gtag.js) - Google Analytics