ASM函数监听实现(一)之简单静态方法注入

1.目的:实现对函数执行监听,在函数调用前,后得到通知。考虑用asm来实现。
2.资料:在网上看到关于asm的技术资料,写了一个简单的实现。参考链接如下:
[url]http://www.cnblogs.com/eafy/archive/2008/06/18/1224633.html[/url]
[url]http://alvinqq.iteye.com/blog/940965[/url]
[url]http://www.ibm.com/developerworks/cn/java/j-lo-asm30/[/url]
[url]http://ayufox.iteye.com/blog/668917[/url]
3.代码:asmAopClassAdapter 该类对目标类进行操作。
public class asmAopClassAdapter extends ClassAdapter{

private String enhancedSuperName,enhancedName;

private String method;

private String startInfo,endInfo;

public asmAopClassAdapter(ClassVisitor cv,String methodName,String start,String end) {
//Responsechain 的下一个 ClassVisitor,这里我们将传入 ClassWriter,
// 负责改写后代码的输出
super(cv);
method = methodName;
startInfo = start;
endInfo = end;
}

// 重写 visitMethod,访问到 "method" 方法时,
// 给出自定义 MethodVisitor,实际改写方法内容
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
MethodVisitor wrappedMv = mv;
if (mv != null) {
if (name.equals(method)) {
wrappedMv = new asmAopMethodAdapter(mv,startInfo,endInfo);
} else if (name.equals("<init>")) {
wrappedMv = new ChangeToChildConstructorMethodAdapter(mv,
enhancedSuperName);
}
}
return wrappedMv;
}

public void visit(final int version, final int access, final String name,
final String signature, final String superName,
final String[] interfaces) {
enhancedName = name.replace("/", "$")+"$EnhancedByASM"; // 改变类命名
enhancedSuperName = name; // 改变父类
super.visit(version, access, enhancedName, signature,
enhancedSuperName, interfaces);
}

public String getEnhancedName() {
return enhancedName;
}
}

asmAopMethodAdapter 对目标类的方法做操作。
public class asmAopMethodAdapter extends MethodAdapter implements Opcodes{

private final static int EXCEPTION_STACK = 2 + 1;//max_stack至少需要能够容纳2个常量地址(监控方法使用)和1个exception地址

private Label try_catch_start,try_catch_end;

private String startInfo,endInfo;

public asmAopMethodAdapter(MethodVisitor mv,String start,String end) {
super(mv);
try_catch_start = new Label();
try_catch_end = new Label();
startInfo = start;
endInfo = end;
}

public void visitCode() {
mv.visitCode();
mv.visitLabel(try_catch_start);

mv.visitLdcInsn(startInfo);
//asmAopInvoker 这里写类的路径例如:com.asm.asmAopInvoker 应写成 com/asm/asmAopInvoker
mv.visitMethodInsn(INVOKESTATIC, "asmAopInvoker",
"methodStart", "(Ljava/lang/String;)V");
}

public void visitInsn(int opcode){
if(opcode >= IRETURN && opcode <= RETURN){
mv.visitLdcInsn(endInfo);
//asmAopInvoker 这里写类的路径例如:com.asm.asmAopInvoker 应写成 com/asm/asmAopInvoker
mv.visitMethodInsn(INVOKESTATIC, "asmAopInvoker",
"methodEnd", "(Ljava/lang/String;)V");
}
mv.visitInsn(opcode);
}
public void visitEnd() {
mv.visitLabel(try_catch_end);
mv.visitTryCatchBlock(try_catch_start, try_catch_end, try_catch_end, null);
mv.visitLdcInsn(endInfo);
//asmAopInvoker 这里写类的路径例如:com.asm.asmAopInvoker 应写成 com/asm/asmAopInvoker
mv.visitMethodInsn(INVOKESTATIC, "asmAopInvoker",
"methodEnd", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.ATHROW);
mv.visitEnd();
}

public void visitMaxs(int maxStack,int maxLocals){
//保证max stack足够大
mv.visitMaxs(Math.max(EXCEPTION_STACK,maxStack), maxLocals);
}
}

ChangeToChildConstructorMethodAdapter该类生成目标类的子类。
public class ChangeToChildConstructorMethodAdapter extends MethodAdapter { 

private String superClassName;

public ChangeToChildConstructorMethodAdapter(MethodVisitor mv,
String superClassName) {
super(mv);
this.superClassName = superClassName;
}

public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
// 调用父类的构造函数时
if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>")) {
owner = superClassName;
}
super.visitMethodInsn(opcode, owner, name, desc);// 改写父类为 superClassName
}
}

asmAopGenerator 一个工具类,生成代理目标类的对象。
public class asmAopGenerator {

private AOPGeneratorClassLoader classLoader ;

public asmAopGenerator(){
classLoader = new AOPGeneratorClassLoader();
}


public Object proxy(Class c,String methodName,String startInfo,String endInfo) {
try{
if( c != null){
String classPach = c.toString().replace("/", ".");
ClassReader cr = new ClassReader(classPach.substring(6,classPach.length()));
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
asmAopClassAdapter classAdapter = new asmAopClassAdapter(cw,methodName,startInfo,endInfo);
cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
byte[] data = cw.toByteArray();
Class obj = classLoader.defineClassFromClassFile(classAdapter.getEnhancedName(), data);
//TODO:隐藏BUG
return obj.newInstance();
}}catch(Exception e){
e.printStackTrace();
}
return null;
}


class AOPGeneratorClassLoader extends ClassLoader {
public Class defineClassFromClassFile(String className,
byte[] classFile) throws ClassFormatError {
return defineClass(className, classFile, 0,
classFile.length);
}
}
}


asmAopInvoker 函数注入类

public class asmAopInvoker {

public static void methodEnd(String evtID){
System.out.println(evtID);
}

public static void methodStart(String evtID){
System.out.println(evtID);
}

}


测试类
public class helloWorld {

public void sayHello(){
System.out.println("helloWorld....");
}

public static void main(String[]args){
asmAopGenerator aag = new asmAopGenerator();
helloWorld hw = (helloWorld) aag.proxy(helloWorld.class, "sayHello", "it's begin", "it's end");
hw.sayHello();
}
}

输出结果

it's begin
helloWorld....
it's end

4.目的基本达成。
5.待改进,在监听函数执行后,是用的try和catch抛异常的方式来实现。
6.环境 asm 3.3.1 jdk 1.6.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值