JAVAssist字節碼操做

Java動態性的兩種常見實現方式java

  • 字節碼操做
  • 反射

 

運行時操做字節碼能夠讓咱們實現以下功能:git

  • 動態生成新的類
  • 動態改變某個類的結構(添加/刪除/修改  新的屬性/方法)

 

優點:github

  • 比反射開銷小,性能高
  • JAVAasist性能高於反射,低於ASM

 

常見的字節碼操做類庫express

BCEL數組

這是Apache Software Fundationjakarta項目的一部分。BCELjavaclassworking普遍使用的一種跨級啊,它能夠讓你深刻JVM彙編語言進行類的操做的細節。BCELjavassist所強調的是源碼級別的工做。app

ASM框架

是一個輕量及java字節碼操做框架,直接涉及到JVM底層的操做和指令。ide

 

CGLIB工具

是一個強大的,高性能,高質量的Code生成類庫,基於ASM實現。性能

 

JAVAssist

 

JAVAssist庫的API

  • javassist最外層的APIJava的反射包中的API頗爲相似、
  • 它主要有CtClass,CtMethod,以及CtField及各種組成。用以執行和JDK反射APIjava.lang.Classjava.lang.reflect.Methodjava.lang.reflect.Method.Field相同的操做。

 

官方介紹:

Javassist (Java Programming Assistant) makes Java bytecode manipulation simple. It is a class library for editing bytecodes in Java; it enables Java programs to define a new class at runtime and to modify a class file when the JVM loads it. Unlike other similar bytecode editors, Javassist provides two levels of API: source level and bytecode level. If the users use the source-level API, they can edit a class file without knowledge of the specifications of the Java bytecode. The whole API is designed with only the vocabulary of the Java language. You can even specify inserted bytecode in the form of source text; Javassist compiles it on the fly. On the other hand, the bytecode-level API allows the users to directly edit a class file as other editors.

 

 

javassist庫的簡單使用

  • 使用一個全新的類
  • 使用XJAD反編譯工具,將生成的class二年間反編譯成JAVA文件

 

例1:

public class Demo01 {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("com.bjsxt.bean.Emp");
        
        //建立屬性
        CtField f1 = CtField.make("private int empno;", cc);
        CtField f2 = CtField.make("private String ename;", cc);
        cc.addField(f1);
        cc.addField(f2);
        
        //建立方法
        CtMethod m1 = CtMethod.make("public int getEmpno(){return empno;}", cc);
        CtMethod m2 = CtMethod.make("public void setEmpno(int empno){this.empno=empno;}", cc);
        cc.addMethod(m1);
        cc.addMethod(m2);
        
        //添加構造器
        CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")}, cc);
        constructor.setBody("{this.empno=empno; this.ename=ename;}");
        cc.addConstructor(constructor);
        
        cc.writeFile("c:/myjava"); //將上面構造好的類寫入到c:/myjava中
        System.out.println("生成類,成功!");
    }
}

javassist庫的API

  • 方法操做
    • 修改已有方法的方法體(插入代碼到已有方法體)
    • 新增方法
    • 刪除方法

 

各類符號替代

$0, $1, $2, ...    

this and actual parameters

$args

An array of parameters. The type of $args is Object[].

$$

All actual parameters.

For example, m($$) is equivalent to m($1,$2,...)

$cflow(...)

cflow variable

$r

The result type. It is used in a cast expression.

$w

The wrapper type. It is used in a cast expression.

$_

The resulting value

$sig

An array of java.lang.Class objects representing the formal parameter types.

$type

A java.lang.Class object representing the formal result type.

$class

A java.lang.Class object representing the class currently edited.

例2:

public class Demo02 {
    /**
     * 處理類的基本用法
     * @throws Exception 
     */
    public static void test01() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");
        
        byte[] bytes = cc.toBytecode();
        System.out.println(Arrays.toString(bytes));
        
        System.out.println(cc.getName()); //獲取類名
        System.out.println(cc.getSimpleName()); //獲取簡要類名
        System.out.println(cc.getSuperclass()); //得到父類
        System.out.println(cc.getInterfaces()); //得到接口
        
    }
    
    /**
     * 測試產生新的方法
     * @throws Exception 
     */
    public static void test02() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.bjsxt.test.Emp");
        
//        CtMethod m = CtNewMethod.make("public int add(int a,int b){return a+b;}", cc);
        
        CtMethod m = new CtMethod(CtClass.intType,"add",
                new CtClass[]{CtClass.intType,CtClass.intType},cc);
        m.setModifiers(Modifier.PUBLIC);
        m.setBody("{System.out.println(\"Hello!!!\");return $1+$2;}");
        
        cc.addMethod(m);
        
        //經過反射調用新生成的方法
        Class clazz = cc.toClass();
        Object obj = clazz.newInstance();  //經過調用Emp無參構造器,建立新的Emp對象
        Method method = clazz.getDeclaredMethod("add", int.class,int.class);
        Object result = method.invoke(obj, 200,300);
        System.out.println(result);
    }
    
    /**
     * 修改已有的方法的信息,修改方法體的內容
     * @throws Exception
     */
    public static void test03() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");
        
        CtMethod cm = cc.getDeclaredMethod("sayHello",new CtClass[]{CtClass.intType});
        //方法執行前
        cm.insertBefore("System.out.println($1);System.out.println(\"start!!!\");");
        cm.insertAt(9, "int b=3;System.out.println(\"b=\"+b);");
        //方法執行後
        cm.insertAfter("System.out.println(\"after!!!\");");
        
        //經過反射調用新生成的方法
        Class clazz = cc.toClass();
        Object obj = clazz.newInstance();  //經過調用Emp無參構造器,建立新的Emp對象
        Method method = clazz.getDeclaredMethod("sayHello", int.class);
        method.invoke(obj, 300);
    }

    /**
     * 屬性的操做
     * @throws Exception
     */
    public static void test04() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");
        
//        CtField f1 = CtField.make("private int empno;", cc);
        CtField f1 = new CtField(CtClass.intType,"salary",cc);
        f1.setModifiers(Modifier.PRIVATE);
        cc.addField(f1);
        
//        cc.getDeclaredField("ename");   //獲取指定的屬性
        
        //增長相應的set和get方法
        cc.addMethod(CtNewMethod.getter("getSalary", f1));;
        cc.addMethod(CtNewMethod.getter("setSalary", f1));;
        
    }
    
    /**
     * 構造方法的操做
     * @throws Exception
     */
    public static void test05() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");
        
        CtConstructor[] cs = cc.getConstructors();
        for (CtConstructor c : cs) {
            System.out.println(c.getLongName());
        }
    }
    
    /**
     * 註解操做
     * @throws Exception
     */
    public static void test06() throws Exception{
         CtClass cc = ClassPool.getDefault().get("com.sinosoft.test.Emp"); 
         Object[] all = cc.getAnnotations();
         Author a = (Author)all[0]; 
         String name = a.name();
         int year = a.year();
         System.out.println("name: " + name + ", year: " + year);

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



/**
 * 註解類
 * 
*/
public @interface Author { 
          String name(); 
           int year();
}

 

 

侷限性:

  • JDK5.0行語法不支持(包括泛型、枚舉),不支持註解修改,但能夠經過底層的javassist類來解決,具體參考javassist.bytecode.annotation
  • 不支持數組的初始化,如String[]{1」,「2},除非只有數組的容量爲1
  • 不支持內部類和匿名類
  • 不支持continuebtreak表達式
  • 對於繼承關係,有些不支持,例如:
class A{}

class extends A{}

class C extends B{}
相關文章
相關標籤/搜索