Java 動態性(4) - 字節碼操做

1.字節碼操做

  • JAVA動態性的兩種常見實現方式

    字節碼操做java

    反射git

  • 運行時操做字節碼可讓咱們實現以下功能

    動態生成新的類github

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

  • 優點

    比反射開銷小,性能高數組

    JAVAasist性能高於反射,低於asmapp

2.常見的字節碼操做類庫

  • BCEL

    Byte Code Engineering Library (BCEL), 這是Apache Software Foundation 的 Jakarta 項目的一部分.BCEL是Java classworking普遍使用的一種框,它可讓您深刻JVM彙編語言進行類操做的細節.BCEL與Javassist有不一樣的處理字節碼方法,BCEL在實際的JVM指令層次上進行操做(BCEI擁有豐富的JVM指令級支持)而Javassist所強調的是源代碼級別的工做框架

  • ASM

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

  • CGLIB(Code Generation Library)

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

  • Javassist

    是一個開源的分析、編輯和建立Jaw字節碼的類庫.性能較ASM差,跟cglib差很少,可是使用簡單.不少開源框架都在使用它性能

    主頁:http://jboss-javassist.github...

3.JAVAssist庫

  • 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-level API allows the users to directly edit a class file as other editors.
  • Aspect Oriented Programming(AOP面向切面編程):Javassist can be a good tool for adding new methods into a class and for inserting before/after/around advice at the both caller and callee sides.
  • Reflection:Ones of applications of Javassist is runtime reflection;Javassist enables Java programs to use a metaobject that controls method calls on base-level objects.No specialized complier or virtual machine are needed.

4.JAVAssist庫的API簡介

  • javaassist的最外層的API和JAVA的反射包中的API頗爲相似
  • 它主要由CtClass,CtMethod,以及CtField幾個類組成.用以執行和JDK反射API中java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method.Field相同的操做(Ct爲Complie Time)

5.JAVAssist庫的簡單使用

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

使用前先導入javassist的jar包
Demo:

/**
 * 使用javassist生成一個新的類
 * @author Matrix42
 *
 */
public class Demo01 {

    public static void main(String[] args) throws Exception{
        
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("com.lorinda.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);
        CtMethod m3 = CtMethod.make("public String getEname(){return ename;}", cc);
        CtMethod m4 = CtMethod.make("public void setEname(String empno){this.ename = ename;}", cc);
        cc.addMethod(m3);
        cc.addMethod(m4);
        
        //添加構造器
        CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")}, cc);
        constructor.setBody("{this.empno=$1;this.ename=$2;}");
        cc.addConstructor(constructor);
        
        //將上面構造好的類寫入到d:/myjava
        cc.writeFile("d:/myjava");
        System.out.println("生成類,成功!");
        
    }

}

建立完成後使用XJAD反編譯就能夠看到源碼了

反編譯源碼:

package com.lorinda.bean;

public class Emp
{
    private int empno;
    private String ename;

    public int getEmpno()
    {
        return empno;
    }

    public void setEmpno(int i)
    {
        empno = i;
    }

    public String getEname()
    {
        return ename;
    }

    public void setEname(String s)
    {
        ename = ename;
    }

    public Emp(int i, String s)
    {
        empno = i;
        ename = s;
    }
}

6.JAVAssist庫的API詳解

  • 方法操做

    修改已有方法的方法體(插入代碼到已有方法體)

    新增方法

    刪除方法

    a b c
    \$0,\$1,\$2,... this and actual parameters \$0 表明的是 this,\$1 表明方法參數的第一個參數,\$2 表明方法參數的第二個參數, 以此類推,$N 表明方法參數的第 N 個參數
    \$args An arrar of parameters The type of $args is Object[], \$args[0] 對應的是 \$1 而不是 \$0
    $$ 全部方法參數的簡寫, 主要用在方法調用上 move(String a,String b) move(\$\$) 至關於 move(\$1,\$2)
    fallthrough path 在類路徑, 源文件路徑等中有不存在的路徑警告
    \$cflow
    \$r
    \$_
    addCatch()
    \$class
    \$sig

  • 屬性操做

    修改已有方法的方法體(插入代碼到已有方法體)

    新增方法

    刪除方法

Demo:

import java.awt.color.CMMException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class Demo02 {

    public static void test01() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("javassist.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()); //得到接口
    }
    
    public static void test02()throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("javassist.Emp");
        
        //CtMethod m = CtMethod.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(\"Ha Ha\");return $1+$2;}");
        
        cc.addMethod(m);
        
        //經過反射調用新生產的方法
        Class<?> clazz = cc.toClass();
        Object obj = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("add", int.class,int.class);
        Object result = method.invoke(obj, 200,300);
        System.out.println(result);
    }
    
    public static void test03()throws Exception{
        
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("javassist.Emp");
        
        CtMethod cm = cc.getDeclaredMethod("sayHello", new CtClass[]{CtClass.intType});
        cm.insertBefore("System.out.println($1);");
        
        Class<?> clazz = cc.toClass();
        Object obj = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("sayHello", int.class);
        method.invoke(obj, 90);
        
    }
    
    public static void test04() throws Exception{
        
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("javassist.Emp");
        
        //CtField f1 = CtField.make("private int empno", cc);
        CtField f1 = new CtField(CtClass.intType,"salary",cc);
        f1.setModifiers(Modifier.PRIVATE);
        cc.addField(f1,"1000");//1000位默認值
        
       // cc.getDeclaredField("ename"); //獲取指定屬性
        
        cc.addMethod(CtNewMethod.getter("salary",f1));
        cc.addMethod(CtNewMethod.setter("salary", f1));
    }
    
    public static void test05()throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("javassist.Emp");
        
        CtConstructor[] cs = cc.getConstructors();
        for (CtConstructor ctConstructor : cs) {
            System.out.println(ctConstructor.getLongName());
        }
    }
    
    public static void main(String[] args) throws Exception {
        //test01();
        //test02();
       // test03();
       // test04();
        test05();

    }

}
  • 構造方法操做

    getConstructors()

  • 註解操做

代碼片斷:

public @interface Author {
String name();
int year();
}
@Author(name="Chiba",year=2005)
public class Point{
    int x,y;
}
CtClass cc = ClassPool.getDefault.get("Point");
Object[] all = cc.getAnnotations();
Author a = (Author)all[0];
String name = a.name();
int year = a.year();
System.out.println("name:"+name+",year:"+year);
當調用了writeFile(),toClass(),toBytecode(),Javassist會把那個CtClass對象凍結,若是想使用凍結的對象能夠調用.defrose()方法
  • 侷限性

    JDK5.0新語法不支持(包括泛型,枚舉),不支持註解修改,單能夠的經過底層javasist類來解決,具體參考:javassist.bytecode.annotation

    不支持數組的初始化,如String[]{"1","2"},除非只有數組容量爲1

    不支持內部類盒匿名類

    不支持continue盒break表達式

    對於繼承關係,有些語法不支持,如:

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