JAVA动态字节码

概述

java动态字节码指的是在java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对代码的二进制文件进行修改。动态java字节码主要是为了减少冗余代码,提高性能。

实现字节码增强的主要步骤:

  1. 修改字节码。在内存中获取到原来的字节码,通过一些工具(如ASMJavaasist)来修改它的byte[]数组,得到一个新的byte[]数组。
  2. 使修改后的字节码生效
    1. 自定义ClassLoader来加载修改后的字节码
    2. 替换掉原来的字节码,在JVM加载用户的Class时,拦截返回修改后的字节码。

用途

  1. 动态生成新的类
  2. 动态修改某个类的结构(添加/删除/修改 新的属性/方法)
  3. AOP技术使用的就是动态字节码技术

字节码操作类库

BCEL

BCEL可以深入JVM汇编语言进行类操作的细节。BCELjavassist有不同的处理字节码方法,BCEL在实际的JVM指令层次上进行操作,而javassist所强调的是源代码级别的工作。

ASM

是轻量级java字节码操作框架,直接涉及到JVM底层的操作和指令。高性能,高质量

CGLB

基于ASM实现

javassist

性能低于ASMCGLB差不多,但是使用简单,很多开源框架都使用的是javassistjavassist比反射开销小,性能高。

javassist的最外层的APIJAVA的反射包中的API颇为相似。它主要由CtClassCtMethod以及CtField几个类组成,用以执行和JDK反射APIjava.lang.Classjava.lang.reflect.Methodjava.lang.reflect.Field相同的操作。

局限性

  1. JDK5.0新语法不支持(包括泛型、枚举),不支持注解修改
  2. 不支持数组的初始化
  3. 不支持内部类和匿名类
  4. 不支持continuebreak表达式
  5. 对于继承关系,有些不支持。

maven : mvnrepository.com/artifact/ja…


反射调用方法

public class User {

    private String name;
    private Integer age;


    public void sum(int a,int b){
        int sum = a + b;
        System.out.println("sum = " + sum);
    }


    public static void main(String[] args) {
        try {
            Class<?> clz = Class.forName("javassist.User");
            Object newInstance = clz.newInstance();
            Method method = clz.getDeclaredMethod("sum", int.class, int.class);
            Object invoke = method.invoke(newInstance, 1, 3);

        }catch (Exception e){

        }
    }
}
复制代码

javassist生成class文件

 //使用javassist创建class文件
 ClassPool pool = ClassPool.getDefault();
 //创建 class 文件
 CtClass userClass = pool.makeClass("com.beisiji.javassist.User");
 //创建 id 属性
 CtField idField = CtField.make("private Integer id;", userClass);
 //创建 name 属性
 CtField nameField = CtField.make("private String name;", userClass);
 //添加属性
 userClass.addField(idField);
 userClass.addField(nameField);
 //创建方法
 CtMethod setIdMethod = CtMethod.make("public void setId(Integer id) { this.id = id; }", userClass);
 CtMethod getIdMethod = CtMethod.make("public Integer getId() { return id; }", userClass);
 //添加方法
 userClass.addMethod(setIdMethod);
 userClass.addMethod(getIdMethod);

//创建构造器
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType, pool.get("java.lang.String")}, userClass);
ctConstructor.setBody("{ this.id = id;this.name = name; }");
userClass.addConstructor(ctConstructor);
userClass.writeFile("C:/Users/yuanl/Desktop/md_dir");
复制代码

javassist修改类文件信息

 //使用javassist修改类文件信息(添加方法)
 ClassPool pool = ClassPool.getDefault();
 //需要加载的类信息(需要修改类信息的全限定名称)
 CtClass userClass = pool.get("executor.ExecutorDemo");
 //创建方法
 CtMethod method = new CtMethod(CtClass.intType, "add", new CtClass[]{CtClass.intType, CtClass.intType}, userClass);
 //设置方法权限
 method.setModifiers(Modifier.PUBLIC);
 method.setBody("{return $1 + $2;}");	//$0:this , $1第一个参数 , $2第二个参数
 userClass.addMethod(method);
 userClass.writeFile("C:/Users/yuanl/Desktop/md_dir");
 
 //调用添加的方法
 Class clz = userClass.toClass();
 Object newInstance = clz.newInstance();
 Method addMethod = clz.getDeclaredMethod("add", int.class, int.class);
 Object invoke = addMethod.invoke(newInstance, 2, 3);
 System.out.println("invoke = " + invoke);
复制代码

猜你喜欢

转载自juejin.im/post/5c693e16f265da2db1560939
今日推荐