JAVA動態位元組碼

yuanlb發表於2019-02-17

概述

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);
複製程式碼

相關文章