1. 程式人生 > >Java動態程式設計之javassist

Java動態程式設計之javassist

概述

  Javassist是一款位元組碼編輯工具,可以直接編輯和生成Java生成的位元組碼,以達到對.class檔案進行動態修改的效果。熟練使用這套工具,可以讓Java程式設計更接近與動態語言程式設計。

教程

maven依賴

pom.xml

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.20.0-GA</version>
</dependency
>

API講解

1、建立一個新的Class

ClassPool pool = ClassPool.getDefault();

//定義類
CtClass stuClass = pool.makeClass("com.ricky.Student");

當然,如果某個類已經存在,可以直接載入它,如下:

CtClass cc = pool.get("java.lang.String");

2、構造類成員變數

//id屬性
CtField idField = new CtField(CtClass.longType, "id", stuClass);
stuClass.addField(idField);

3、構造類方法

CtMethod getMethod = CtNewMethod.make("public int getAge() { return this.age;}", stuClass);

CtMethod setMethod = CtNewMethod.make("public void setAge(int age) { this.age = age;}", stuClass);

stuClass.addMethod(getMethod);
stuClass.addMethod(setMethod);

應用示例

1、動態構造一個Class

package com
.ricky.codelab.javassist; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.NotFoundException; /** * 動態構造Class * @author Ricky * */ public class JavassistDemo { public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException { ClassPool pool = ClassPool.getDefault(); //定義類 CtClass stuClass = pool.makeClass("com.ricky.Student"); //載入類 // CtClass cc = pool.get(classname); //id屬性 CtField idField = new CtField(CtClass.longType, "id", stuClass); stuClass.addField(idField); //name屬性 CtField nameField = new CtField(pool.get("java.lang.String"), "name", stuClass); stuClass.addField(nameField); //age屬性 CtField ageField = new CtField(CtClass.intType, "age", stuClass); stuClass.addField(ageField); CtMethod getMethod = CtNewMethod.make("public int getAge() { return this.age;}", stuClass); CtMethod setMethod = CtNewMethod.make("public void setAge(int age) { this.age = age;}", stuClass); stuClass.addMethod(getMethod); stuClass.addMethod(setMethod); // stuClass.writeFile("F:\\Practice_Demo"); Class<?> clazz = stuClass.toClass(); System.out.println("class:"+clazz.getName()); System.out.println("------------屬性列表------------"); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getType()+"\t"+field.getName()); } System.out.println("------------方法列表------------"); //方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method: methods){ System.out.println(method.getReturnType()+"\t"+method.getName()+"\t"+Arrays.toString(method.getParameterTypes())); } } }

2、指定父類

package com.ricky.codelab.javassist;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;

public class JavassistExtendDemo {

    public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException {

        ClassPool pool = ClassPool.getDefault();

        //定義類
        CtClass stuClass = pool.makeClass("com.ricky.Student");

        //設定父類
        stuClass.setSuperclass(pool.get("com.ricky.codelab.javassist.domain.Person"));

        //hobbies屬性
        CtField ageField = new CtField(pool.getCtClass("java.util.List"), "hobbies", stuClass);
        stuClass.addField(ageField);

        Class<?> clazz = stuClass.toClass();
        System.out.println("class:"+clazz.getName());

        System.out.println("------------屬性列表------------");
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getType()+"\t"+field.getName());
        }

        System.out.println("------------方法列表------------");
        //方法
        Method[] methods = clazz.getMethods();
        for (Method method: methods){
            System.out.println(method.getReturnType()+"\t"+method.getName()+"\t"+Arrays.toString(method.getParameterTypes()));
        }

    }

}

3、動態注入程式碼

package com.ricky.codelab.javassist;

import java.io.IOException;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;

/**
 * 動態注入程式碼
 * 
 * @author Ricky
 *
 */
public class JavassistInsertDemo {

    public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException, InstantiationException, IllegalAccessException {

        ClassPool pool = ClassPool.getDefault();

        // 定義類
        CtClass ctClass = pool.get("com.ricky.codelab.javassist.Calculator");

        // 需要修改的方法名稱
        String mname = "getSum";
        CtMethod mold = ctClass.getDeclaredMethod(mname);
        // 修改原有的方法名稱
        String nname = mname + "$impl";
        mold.setName(nname);

        //建立新的方法,複製原來的方法
        CtMethod mnew = CtNewMethod.copy(mold, mname, ctClass, null);
        // 主要的注入程式碼
        StringBuffer body = new StringBuffer();
        body.append("{\nlong start = System.currentTimeMillis();\n");
        // 呼叫原有程式碼,類似於method();($$)表示所有的引數
		body.append(nname + "($$);\n");
        body.append("System.out.println(\"Call to method " + mname
                + " took \" +\n (System.currentTimeMillis()-start) + " + "\" ms.\");\n");

        body.append("}");
        // 替換新方法
        mnew.setBody(body.toString());
        // 增加新方法
        ctClass.addMethod(mnew);

        Calculator calculator =(Calculator)ctClass.toClass().newInstance(); 

        calculator.getSum(10000);
    }

}

class Calculator {

    public void getSum(long n) {
        long sum = 0;
        for (int i = 0; i < n; i++) {
            sum += i;
        }
        System.out.println("n="+n+",sum="+sum);
    }
}