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.
Javassist(Java Programming Assistant)使字節(jié)碼操作更加簡單。它是一個用來編輯Java字節(jié)碼的類庫,可以用來在JVM加載類的時候運行時創(chuàng)建 Java類及修改class文件。其功能與jdk自帶的反射功能類似,但比反射功能更強大。
常用類
ClassPool
是CtClass對象的容器,所有的CtClass都必須從這個對象中加載。使用ClassPool 類可以跟蹤和控制所操作的類,它的工作方式與 JVM 類裝載器非常相似;CtClass
一個CtClass代表一個class,它必須從ClassPool 獲取。CtClass提供了檢查類數據(如字段和方法)以及在類中添加新字段、方法和構造函數、以及改變類、父類和接口的方法。不過,Javassist 并未提供刪除類中字段、方法或者構造函數的任何方法;CtField
用來訪問域CtMethod
用來訪問方法CtConstructor
用來訪問構造器toClass()
這個方法會將這個class轉換為java.lang.Class對象。一旦這個方法被調用了,就不再允許對這個class進行其他的修改了。這個方法會使用當前線程上下文中的class loader來加載class。如果程序是在一些應用服務器運行的,那么使用上下文的class loader加載class可能會出現(xiàn)錯誤。這個方法只是為了方便而提供的,如果需要更復雜的功能那就需要編寫自己的class loader。
依賴javassist
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.20.0-GA</version>
</dependency>
示例1 動態(tài)創(chuàng)建class
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import org.junit.Before;
import org.junit.Test;
/**
* Javassist是一款字節(jié)碼編輯工具,同時也是一個動態(tài)類庫,它可以直接檢查、修改以及創(chuàng)建 Java類。
* 以下例子就是創(chuàng)建一個動態(tài)類
*
*/
public class CompilerByJavassist {
public Class createClass() throws Exception{
// ClassPool:CtClass對象的容器
ClassPool pool = ClassPool.getDefault();
// 通過ClassPool生成一個public新類Emp.java
CtClass ctClass = pool.makeClass("com.study.javassist.Emp");
// 添加屬性
// 首先添加屬性private String ename
CtField enameField = new CtField(pool.getCtClass("java.lang.String"),
"ename", ctClass);
enameField.setModifiers(Modifier.PRIVATE);
ctClass.addField(enameField);
// 其次添加熟悉privtae int eno
CtField enoField = new CtField(pool.getCtClass("int"), "eno", ctClass);
enoField.setModifiers(Modifier.PRIVATE);
ctClass.addField(enoField);
// 為屬性ename和eno添加getXXX和setXXX方法
ctClass.addMethod(CtNewMethod.getter("getEname", enameField));
ctClass.addMethod(CtNewMethod.setter("setEname", enameField));
ctClass.addMethod(CtNewMethod.getter("getEno", enoField));
ctClass.addMethod(CtNewMethod.setter("setEno", enoField));
// 添加構造函數
CtConstructor ctConstructor = new CtConstructor(new CtClass[] {},
ctClass);
// 為構造函數設置函數體
StringBuffer buffer = new StringBuffer();
buffer.append("{\n").append("ename=\"yy\";\n").append("eno=001;\n}");
ctConstructor.setBody(buffer.toString());
// 把構造函數添加到新的類中
ctClass.addConstructor(ctConstructor);
// 添加自定義方法
CtMethod ctMethod = new CtMethod(CtClass.voidType, "printInfo",
new CtClass[] {}, ctClass);
// 為自定義方法設置修飾符
ctMethod.setModifiers(Modifier.PUBLIC);
// 為自定義方法設置函數體
StringBuffer buffer2 = new StringBuffer();
buffer2.append("{\nSystem.out.println(\"begin!\");\n")
.append("System.out.println(ename);\n")
.append("System.out.println(eno);\n")
.append("System.out.println(\"over!\");\n").append("}");
ctMethod.setBody(buffer2.toString());
ctClass.addMethod(ctMethod);
//最后生成一個class
Class<?> clazz = ctClass.toClass();
// 把生成的class文件寫入文件
byte[] byteArr = ctClass.toBytecode();
FileOutputStream fos = new FileOutputStream(new File("E://Emp.class"));
fos.write(byteArr);
fos.close();
return clazz;
}
/**
* @Author pengyunlong
* @Description 動態(tài)創(chuàng)建
* @param
* @Date 2018/6/15 11:36
*/
@Test
public void testInvoke() throws Exception {
Class<?> aClass = null;
try {
aClass = Class.forName("com.study.javassist.Emp");
}catch (ClassNotFoundException ex){
aClass = createClass();
}
Object obj = aClass.newInstance();
//反射 執(zhí)行方法
obj.getClass().getMethod("printInfo", new Class[] {})
.invoke(obj, new Object[] {});
}
/**
* @Author pengyunlong
* @Description 動態(tài)修改類,只能修改還未加載的類
* @param
* @Date 2018/6/15 11:36
*/
@Test
public void testModify() throws Exception {
CtClass ctClass = ClassPool.getDefault().get("com.soa.other.compiler.Emp");
CtMethod method = ctClass.getDeclaredMethod("printInfo");
method.setBody("System.out.println(\"New method!\");");
ctClass.toClass();
Emp emp = new Emp();
emp.printInfo();
}
@Test
public void testLoaderDir() throws Exception {
URLClassLoader urlClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
for (URL url:urlClassLoader.getURLs()) {
System.out.println(url.toString());
}
}
}
- 運行
testInvoke()
begin!
yy
1
over!
- 生成字節(jié)碼到D://Emp.class,用反編譯工具打開
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.study.javassist;
public class Emp {
private String ename = "yy";
private int eno = 1;
public String getEname() {
return this.ename;
}
public void setEname(String var1) {
this.ename = var1;
}
public int getEno() {
return this.eno;
}
public void setEno(int var1) {
this.eno = var1;
}
public Emp() {
}
public void printInfo() {
System.out.println("begin!");
System.out.println(this.ename);
System.out.println(this.eno);
System.out.println("over!");
}
}
示例2 動態(tài)修改class
package com.soa.other.compiler;
public class Student {
private int age =10;
private String name="Jack";
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printInfo(){
System.out.println("begin!");
System.out.println(name);
System.out.println(age);
System.out.println("over!");
}
}
package com.soa.other.compiler;
/**
* Javassist是一款字節(jié)碼編輯工具,同時也是一個動態(tài)類庫,它可以直接檢查、修改以及創(chuàng)建 Java類。
* 以下例子就是創(chuàng)建一個動態(tài)類
*
*/
public class CompilerByJavassist {
/**
* @Author pengyunlong
* @Description 動態(tài)修改類,只能修改還未加載的類
* @param
* @Date 2018/6/15 11:36
*/
@Test
public void testModify() throws Exception {
CtClass ctClass = ClassPool.getDefault().get("com.soa.other.compiler.Student");
CtMethod method = ctClass.getDeclaredMethod("printInfo");
method.setBody("System.out.println(\"New method!\");");
ctClass.toClass();
Student student = new Student();
student.printInfo();
}
}
- 輸出結果
New method!