首页 > 其他 > 详细

ASM在运行时通过修改字节码动态给接口添加Annotation

时间:2016-05-25 02:14:21      阅读:1811      评论:0      收藏:0      [点我收藏+]

1. 对接口预先添加一个自定义注解:@ServiceType

?

2. 系统启动时,通过Spring扫描有包含注解:@ServiceType的接口,然后通过ASM类库修改接口字节码

?

3. 用到一些辅助工具:java命令,asm-util.jar,asm.jar

============================================================

具体操作:

IIndividualTax接口代码如下(import相关代码省略):

@ServiceType

public interface IIndividualTax {

BusinessResponse request(BusinessRequest request) throws GatewayException;

}

?

目的是要在IIndividualTax?接口上添加注解,并且删掉ServiceType注解,以及在方法request上添加注解,最终想要运行时的代码如下(import相关代码省略):

@Path("it")

@Consumes({"application/json", "text/xml"})

@Produces({"application/json; charset=UTF-8", "text/xml; charset=UTF-8"})

public abstract interface IIndividualTax

{

? @POST

? @Path("request")

? BusinessResponse request(BusinessRequest paramBusinessRequest)?throws GatewayException;

}

?

首先:java -classpath D:\;D:\asm-util-5.1.jar;D:\asm-5.1.jar;. org.objectweb.asm.util.ASMifier cn.com.*.*.gateway.service.IIndividualTax

运行这个命令,需要注意的是classpath参数以及,在当前cmd位置下要有IIndividualTax这个类的全包路径

借助ASMifier类,可以看到这个接口IIndividualTax修改前完整的字节码,如下:

package asm.*****************************.service;

import java.util.*;

import org.objectweb.asm.*;

public class IIndividualTaxDump implements Opcodes {

?

public static byte[] dump () throws Exception {

?

ClassWriter cw = new ClassWriter(0);

FieldVisitor fv;

MethodVisitor mv;

AnnotationVisitor av0;

?

cw.visit(V1_7, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "cn/com/*/*/gateway/service/IIndividualTax", null, "java/lang/Object", null);

?

{

av0 = cw.visitAnnotation("Lcn/com/*/*/gateway/service/ServiceType;", false);

av0.visitEnd();

}

{

mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "request", "(Lcn/com/*/*/gateway/dto/BusinessRequest;)Lcn/com/*/*/gateway/dto/BusinessResponse;", null, new String[] { "cn/com/*/*/gateway/exception/GatewayException" });

mv.visitEnd();

}

cw.visitEnd();

?

return cw.toByteArray();

}

}

?=================================================================================================================

?

在次运行:java -classpath D:\;D:\asm-util-5.1.jar;D:\asm-5.1.jar;. org.objectweb.asm.util.ASMifier cn.com.*.*.gateway.service.IIndividualTax,查看想要修改后接口完整的字节码,如下:

package asm.*************************************service;

import java.util.*;

import org.objectweb.asm.*;

public class IIndividualTaxDump implements Opcodes {

?

public static byte[] dump () throws Exception {

?

ClassWriter cw = new ClassWriter(0);

FieldVisitor fv;

MethodVisitor mv;

AnnotationVisitor av0;

?

cw.visit(V1_7, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "cn/com/*/*/gateway/service/IIndividualTax", null, "java/lang/Object", null);

?

{

av0 = cw.visitAnnotation("Ljavax/ws/rs/Path;", true);

av0.visit("value", "it");

av0.visitEnd();

}

{

av0 = cw.visitAnnotation("Ljavax/ws/rs/Consumes;", true);

{

AnnotationVisitor av1 = av0.visitArray("value");

av1.visit(null, "application/json");

av1.visit(null, "text/xml");

av1.visitEnd();

}

av0.visitEnd();

}

{

av0 = cw.visitAnnotation("Ljavax/ws/rs/Produces;", true);

{

AnnotationVisitor av1 = av0.visitArray("value");

av1.visit(null, "application/json; charset=UTF-8");

av1.visit(null, "text/xml; charset=UTF-8");

av1.visitEnd();

}

av0.visitEnd();

}

{

av0 = cw.visitAnnotation("Lcn/com/*/*/gateway/service/ServiceType;", false);

av0.visitEnd();

}

{

mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "request", "(Lcn/com/*/*/gateway/dto/BusinessRequest;)Lcn/com/*/*/gateway/dto/BusinessResponse;", null, new String[] { "cn/com/*/*/gateway/exception/GatewayException" });

{

av0 = mv.visitAnnotation("Ljavax/ws/rs/POST;", true);

av0.visitEnd();

}

{

av0 = mv.visitAnnotation("Ljavax/ws/rs/Path;", true);

av0.visit("value", "request");

av0.visitEnd();

}

mv.visitEnd();

}

cw.visitEnd();

?

return cw.toByteArray();

}

}

?

?

===================================================================================================================

?

对于新手玩ASM不熟悉的情况,通过对比上面的二段字节码存在的差异,用ASM提供的API进行码代码,生成运行时的关键代码如下:

?

public class TaxVisitor extends ClassVisitor implements Opcodes{

?

public TaxVisitor(int api, ClassVisitor cv) {

super(api, cv);

}

?

@Override

public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {

MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);

if (name.equals("request")) {

AnnotationVisitor ?av1 = mv.visitAnnotation("Ljavax/ws/rs/POST;", true);//在request上添加@POST注解,对比看上面红色粗体类源码

AnnotationVisitor ?av2 = mv.visitAnnotation("Ljavax/ws/rs/Path;", true);//同上

av2.visit("value", "request");

av2.visitEnd();//这些api用法,查看asm相关说明

av1.visitEnd();

}

?

return mv;

}

?

@Override

public AnnotationVisitor visitAnnotation(String name, boolean arg1) {

if (name.equals("Lcn/com/*/*/gateway/service/ServiceType;")) {

return null;//返回null说明,要删掉接口上@ServiceType注解

}

?

return super.visitAnnotation(name, arg1);

}

}

?

=====================================================================================================

public class AsmTest extends ClassLoader{

?

public static void main(String[] args) throws Exception {

?

ClassReader cr=new ClassReader(IIndividualTax.class.getName());

ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS);

?

AnnotationVisitor av0 = cw.visitAnnotation("Ljavax/ws/rs/Path;", true);//在接口上添加注解@Path

av0.visit("value", "it");

av0.visitEnd();

?

AnnotationVisitor av1 = cw.visitAnnotation("Ljavax/ws/rs/Consumes;", true);//在接口上添加注解@Consumes

AnnotationVisitor av2 = av1.visitArray("value");

av2.visit(null, "application/json");

av2.visit(null, "text/xml");

av2.visitEnd();

av1.visitEnd();

?

AnnotationVisitor av3 = cw.visitAnnotation("Ljavax/ws/rs/Produces;", true);//在接口上添加注解@Produces

AnnotationVisitor av4 = av3.visitArray("value");

av4.visit(null, "application/json; charset=UTF-8");

av4.visit(null, "text/xml; charset=UTF-8");

av4.visitEnd();

av3.visitEnd();

?

TaxVisitor myv=new TaxVisitor(Opcodes.ASM4,cw);

cr.accept(myv, 0);

?

byte[] code=cw.toByteArray();//最终想要类的字节码

?

AsmTest loader=new AsmTest();

Class<?> appClass=loader.defineClass(null, code, 0,code.length);//字节码加载到jvm

System.out.println(AnnotationUtils.findAnnotation(appClass, Path.class));

FileOutputStream fos = new FileOutputStream(appClass.getResource("").getPath()+"/IIndividualTax.class");//覆盖当前class文件

fos.write(code);

fos.close();

?

}

}

?

注意:对类或接口的字节码的修改必须用ClassWriter的实例进行api相关操作,对类的方法,属性字节码操作需要通过对应的Visitor操作,最终调用visitEnd()

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

ASM在运行时通过修改字节码动态给接口添加Annotation

原文:http://strongant.iteye.com/blog/2299876

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!