`
xiefeifeihu
  • 浏览: 97314 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

用ASM实现AOP

阅读更多

AOP的关键在于拦截,如果在代码中直接写入要插入的代码则是最直接的AOP。这当然不是指在source中生写代码,而是希望在程序员不知觉的情况下修改了代码。

asm是个开源包,可以很方便地读写class的bytecode。网站是http://asm.ow2.org/。为了方便修改类建议下载Eclipse插件。

使用方法挺简单。首先实现一个ClassAdapter导出类,找到要修改的函数:

public class SOClassAdapter extends ClassAdapter {

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

	@Override
	public MethodVisitor visitMethod(int access, String name, String desc,
			String signature, String[] exceptions) {
		if (shouldModify(name, parasFieldName(name))) {
			MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
					exceptions);
			return new ModifyMethodAdapter(mv);
		}
		return super.visitMethod(access, name, desc, signature, exceptions);
	}

略。

在visitMethod中找到要修改的函数后,通过实现一个MethodAdapter的导出类修改函数,例子如下:

public class ModifyMethodAdapter extends MethodAdapter {

	public ModifyMethodAdapter(MethodVisitor mv) {
		super(mv);
	}

	@Override
	public void visitInsn(int opcode) {
		if (opcode == Opcodes.RETURN) {
			visitVarInsn(Opcodes.ALOAD, 0);
			// visitVarInsn(Opcodes.ALOAD, 1);
			visitMethodInsn(Opcodes.INVOKESTATIC, "asm/TopClass", "print",
					"(Lasm/TopClass;)V");// (Lasm/Bean;)V
		}
		super.visitInsn(opcode);
	}
}
它在函数return前加了一句:asm.TopClass.print(this)静态函数。

最后通过ClassReader读入类,ClassAdapter访问并修改类的字节码,通过ClassWriter回写类:

		ClassReader cr = new ClassReader("asm/Bean");//这是通过系统的ClassLoader加载类
		ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
		ClassAdapter claAdapter = new SOClassAdapter(cw);
		cr.accept(claAdapter, ClassReader.SKIP_DEBUG);
		byte[] data = cw.toByteArray();//得到修改后的字节码

可以将修改后的类回写到class文件中:

		String path = c.getResource(c.getSimpleName() + ".class").getPath();
		File file = new File(path);

		FileOutputStream fout = new FileOutputStream(file);
		fout.write(data);
		fout.close();
过程就这么简单。这里的难点在于如何修改类。有一个小技巧就是先在source中临时改写类,再用asm的Eclipse插件看类的bytecode,将其copy到MethodAdapter即可。这样就不必花精力了解java类的字节码。

到此我们已经知道如何去修改类了,但是怎么用呢,这个问题折腾了我好几天。最终确定了两个较简单的方案:

方法一:编译后重写class文件

可以写一个main方法,寻找要修改的类,然后用ClassAdapter重定义类,得到byte[],再用FileOutputStream把字节流写入文件。这个操作完成之后剩下的就是如何把编译、改类、打包、运行一系列流程整合起来。我们用的编译工具是maven:

在pom文件里加一个插件:

			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<version>1.1</version>
				<executions>
					<execution>
						<phase>main</phase>
						<goals>
							<goal>java</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<mainClass>asm.Redefine</mainClass>
					<argument>-classpath</argument>
					<classpath />
				</configuration>
			</plugin>
编译打包时执行命令:“clean compile exec:java package”即可。

方法二:运行时在内存中重定义类

这种方法需要用到java5的Instrumentation。首先写一个premain方法:

public class PreMain {

	public static Instrumentation inst;

	public static void premain(String agentArgs, Instrumentation inst) {
		PreMain.inst = inst;
		System.out.println("获取inst----" + inst);
	}

}

将Instrumentation实例保存到static变量中。然后将PreMain类打成jar包(如agent.jar),在MANIFEST.MF中添加一下内容:

Manifest-Version: 1.0
Class-Path: .
Premain-Class: asm.PreMain
Can-Redefine-Classes: true

在虚拟机启动时添加参数:-javaagent:路径/agent.jar。tomcat是在catalina.bat/catalina.sh中加上一句:“SET JAVA_OPTS=%JAVA_OPTS% -javaagent:%CATALINA_HOME%/lib/agent.jar”
然后在自己的类中通过反射获取Instrumentation实例,在用它的redefineClasses重定义类:

			Class<?> premainClass = Class
					.forName("asm.PreMain");
			Field field = premainClass.getField("inst");
			Instrumentation inst = (Instrumentation) field.get(premainClass
					.newInstance());
			log.info("从外部获取的Instrumentation(如果为null,则不会重定义类)----" + inst);
			if (null == inst)
				return;

			List<ClassDefinition> definitions = redefineClass();
			inst.redefineClasses(definitions
					.toArray(new ClassDefinition[definitions.size()]));
 

这两种方法适用于不同的场合,可酌情选择使用。

有一点需要注意:ClassReader读取类的时候,如果用new ClassReader(“类名”)则是从系统ClassLoader中读类,这样很可能找不到类
。Tomcat的是WebappClassLoader。不要紧,ClassReader还提供了一个构造函数:
public ClassReader(final InputStream is) throws IOException。
可以将读好的类的字节流传进来。获取字节流的方式可以用
clazz.getResourceAsStream(clazz.getSimpleName() + ".class")
或者clazz.getClassLoader.getResourceAsStream。
分享到:
评论

相关推荐

    深入字节码 -- 使用 ASM 实现 AOP1

    深入字节码 -- 使用 ASM 实现 AOP1

    AOP 的利器 ASM 3.0

    介绍ASM的简单使用和基本原理,并对AOP的原理进行了详细的描述和总结。

    AOP 的利器:ASM 3_0 介绍

    AOP 的利器:ASM 3_0 介绍,很好的书

    AOP的实现代码

    详细介绍了AOP的核心功能(拦截功能)在底层是如何实现的;介绍了两种实现AOP的动态代理:jdk动态代理和cglib动态代理,并详细描述了它们在代码层面的实现。附有必须的cglib.jar和asm.jar

    asm5.0安装包

    asm是assembly的缩写,是汇编的称号,对于java而言,asm就是字节码级别的编程。 而这里说到的asm是指objectweb asm,一种.class的代码生成器的开源项目....现在挺多流行的框架都使用到了asm.所以从aop追溯来到了这。

    ASM字节码操作简单实例

    一个简单的通过ASM修改字节码实现AOP功能的实例,简单易懂,可运行...

    ASM4使用指南

    ASM4使用指南, 插桩技术.android开发AOP开发必备指南.

    ASM操作字节码,动态生成Java类class文件

    ASM操作字节码,动态生成Java类class文件,模拟Spring的AOP实现原理。

    Spring mvc Aop+annotation实现系统日志记录功能实现的jar包

    Spring mvc Aop+annotation实现系统日志记录功能实现的jar包asm-3.3.jar ,aspectjrt.jar , aspectjweaver.jar , cglib-nodep-2.1_3.jar , spring-aop.jar

    asm + cglib demo

    asm 字节码操作工具库的代码demo 以及cglib实现简单aop功能的代码demo

    界面浮标:ASM&AOP。 安全地调用Java接口中声明的方法

    Interface- buoy是一个Android gradle插件,使用ASM在构建过程中动态修改Java字节码。 结合使用动态代理和反射来获得与“?”相同的效果。 影响。 请注意,我们只修改了Java接口中定义的方法,该方法将在运行时触发...

    asm-all-3.2.jar

    asm-all-3.2.jar 全集满足spring aop 开发过程中遇到的问题

    spring3aop所用jar包

    spring3aop所用jar包,可以解决和hibernate3的asm.jar、asm-attrs.jar、cglib-2.1.3.jar这几个jar冲突问题。使用时删掉hibernate的jar,用rar中的替换即可

    ASM Java字节码操作框架

    ASM Java字节码操作框架PPT,结合已有AOP实现方法,对比所有对Java字节码操作方法做比较

    Android AOP之ASM技术研发1

    第二个是访问权限,设为公共 第三个参数类的全路径+类名 第四个参数 第五个参数,类都是Object 第六个为该类实现的接口数组

    cglib aop spring 动态代理

    静态代理--不适合企业开发,适合初学者理解代理。 jdk动态代理--适合企业级开发,但是它要求必须面向接口编程,假如目标类没有实现接口...spring 的AOP功能中 会根据目标类是否实现了接口来判断使用 jdk Proxy还是cglib

    spring AOP aspectjrt

    spring AOP aspectjrt 自带的所有需要包 aspectjrt-1.6.9.jar aspectjweaver-1.6.9.jar cglib-nodep-2.2.jar spring-2.5.6.jar asm-2.2.3.jar asm.commons-2.2.3.jar asm.util-2.2.3.jar

    asm-5.1.6.jar

    这是spring aop的一个依赖包这是spring aop的一个依赖包这是spring aop的一个依赖包这是spring aop的一个依赖包这是spring aop的一个依赖包这是spring aop的一个依赖包

    Java-AOP-Example:AOP测试

    JAVA AOP功能的简单实现1.aoplearn项目中使用Proxy实现AOP功能2.asmlearn项目中使用ASM实现简单类

    QTracer源码分析.pdf

    1.介绍了 Instrument 相关技术名词以及概念;... 本文是该系列的第二篇,主要包含以下内容: 代码实战,在 ASM 修改字节码的基础上实现真正的 AOP; 通过 QTrace 中部分代码分析,了解 QTrace 是如何实现代码插桩

Global site tag (gtag.js) - Google Analytics