`
strongant
  • 浏览: 66882 次
  • 性别: Icon_minigender_1
  • 来自: IT
社区版块
存档分类
最新评论

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

阅读更多

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()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0
1
分享到:
评论

相关推荐

    深入字节码 -- ASM 关键接口 ClassVisitor1

    【深入字节码 -- ASM 关键接口 ClassVisitor1】这篇技术文章主要探讨了ASM库在处理Java字节码时的核心接口ClassVisitor及其相关方法。ASM是一个强大的Java字节码操控和分析框架,常用于动态代理、字节码增强以及AOP...

    asm-util-3.2.jar.zip

    1. 字节码操作:理解Java字节码的概念,知道如何通过ASM生成或修改字节码。 2. 类加载机制:了解Java虚拟机的类加载过程,因为ASM库通常与类加载器配合工作。 3. 类结构:熟悉Java类的内部结构,包括字段、方法、...

    Java ASM,learn-java-asm-main.zip

    Java ASM 是一个强大的字节码操控和分析框架,它允许你在运行时动态生成类和接口,或者修改已存在的类。ASM 提供了低级别的访问,让你可以深入到 Java 类的内部结构,这对于创建代码生成器、编译器、性能监视工具等...

    Java重定义接口实现代码的自动注入.pdf

    2. **字节码操作**:使用如ASM、ByteBuddy等字节码库,能够在运行时动态生成或修改类的字节码,添加新的方法或者改变现有方法的行为。 3. **类装载器(Class Loader)**:Java允许自定义类装载器,可以控制类的加载...

    bytebuddy 字节码增强 创建注解

    在Java开发中,有时我们需要在运行时动态地修改或增强类的行为,这通常是通过字节码操作来实现的。ByteBuddy是一个强大的字节码库,它允许开发者在不使用Java代理(Java Proxy)或者ASM等底层字节码库的情况下,便捷...

    手写组件化框架路由跳转功能

    Apt通常用于预编译阶段生成路由表,Javassist和ASM可能用于运行时的动态类生成和字节码操作,以实现在组件之间灵活的导航。通过这些技术的组合,你可以构建一个高效且灵活的组件化路由系统。 总结来说,Apt、...

    吴天雄-Java注解及动态性详解.doc

    3. 动态Java字节码操作:通过ASM、Javassist等库,可以在运行时生成、修改或分析字节码,实现动态代码生成和热修复等功能。 总的来说,Java注解和动态性极大地扩展了Java的应用场景,提高了代码的灵活性和可维护性...

    比XML更好用的JavaAnnotation2022优秀文档.ppt

    - Javassist和ASM:这两个库允许在运行时动态修改类,包括添加或修改注解,这对于AOP(面向切面编程)和其他动态代理技术非常有用。 - CDI(Contexts and Dependency Injection):是Java EE中的一个组件,使用注解...

    lombok 实现原理(代码)

    它利用了Java的反射API,通过ASM库(一个流行的字节码操作库)在字节码层面插入所需的方法。 5. IDE集成 为了在IDE中得到更好的支持,Lombok提供了IDE插件,如Eclipse和IntelliJ IDEA的插件。这些插件在编写代码时...

    java5.0新特性总结--Annotation系统学习整理笔记

    3. 字节码处理:如使用ASM、ByteBuddy等库,可以在字节码层面处理注解,实现更底层的控制。 五、元注解 元注解是用于注解其他注解的注解,Java 5.0提供了以下几种元注解: 1. `@Retention`:定义注解的生命周期,...

    Java基础总结

    1. **代理原理**:Cglib代理是一种基于ASM字节码生成库对现有类进行子类化处理的代理方式。它主要适用于那些没有实现任何接口的类。 2. **代理对象的创建**: ```java BookFacadeCglib cglib = new ...

    spring配置事务

    CGLIB在生成子类时,就需要使用ASM来解析和生成字节码。因此,ASM是CGLIB的依赖库。 在实际项目中,为了使用Spring的事务管理,我们需要在项目的类路径下包含这些jar包,并在Spring配置文件中正确配置事务管理器,...

    java高级应用简笔

    字节码操作是指对已存在的类文件进行修改或动态生成新类的过程。这种技术广泛应用于框架开发、动态代理等领域。 **5.2 常见字节码操作类库** - **BCEL(Byte Code Engineering Library)**:提供了一种基于JVM汇编...

    VMTranslator

    反射允许程序在运行时检查和修改自身的结构和行为,动态代理可以创建接口的代理实现,而注解处理器则可以在编译时生成额外的代码或资源。 此外,为了高效地转换字节码,VMTranslator可能依赖于ASM、BCEL、JaCoCo等...

    2024-spring-aop学习项目

    ASM字节码操作则允许在运行时对类进行修改,实现更灵活的切面织入。 四、AOP实战 1. 配置切面:在Spring配置文件中声明切面,定义切入点表达式,指定何时何地执行通知。 2. 定义通知:编写包含业务逻辑的通知方法,...

    Spring框架 一个AOP程序.zip

    在Spring中,AOP的实现方式有两种:基于代理(Proxy-based AOP)和基于ASM字节码生成(AspectJ LTW - Load-Time Weaving)。基于代理的AOP使用JDK动态代理或CGLIB代理创建目标对象的代理,而AspectJ LTW则在运行时...

    Spring事务annotation原理详解

    Spring的事务管理是通过AOP来实现的,使用ASM来操作Java字节码,来实现对方法的前后事务管理。在方法调用前,Spring会检查是否有@Transactional注解,如果有,就开启事务,在方法调用后,如果方法执行成功,Spring...

    auto-code.rar

    5. **代码生成库**:如Apache Commons BCEL、ASM或Javassist等库,可以直接操作Java字节码,用于创建或修改类,这在生成动态代码或元编程中非常有用。 6. **领域特定语言(DSL)**:有时,开发者会创建自己的DSL,用...

    spring-core.pdf 源码电子书

    ASM是一个Java字节码操作和分析框架,它能被用来动态生成类或增强既有类的功能。在Spring框架中,ASM被用于实现AOP(面向切面编程)功能,尤其是处理注解和AOP代理的生成。 3. 源码版权声明:注释部分明确指出了...

    fastquery:FastQuery(快速数据库查询的方法)基于Java语言。他的使命是:简化Java操作数据层。做为一个开发者,只需只需要设计DAO接口即可,在项目初始化阶段采用ASM生成好实现类

    在项目初始化阶段采用ASM生成好字节码,因此支持编译前预备,可替换减少运行期的错误,显着提升程序的强壮性 支持安全查询,防止SQL注入 支持与主流数据库连接池框架集成 支持@Query查询,使用@Cond

Global site tag (gtag.js) - Google Analytics