Javassist
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)
使字节码操作更加简单。它是一个用来编辑Java字节码的类库,可以用来在JVM加载类的时候运行时创建 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可能会出现错误。这个方法只是为了方便而提供的,如果需要更复杂的功能那就需要编写自己的class loader。
依赖javassist
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.20.0-GA</version>
</dependency>
示例1 动态创建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是一款字节码编辑工具,同时也是一个动态类库,它可以直接检查、修改以及创建 Java类。
* 以下例子就是创建一个动态类
*
*/
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 动态创建
* @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();
//反射 执行方法
obj.getClass().getMethod("printInfo", new Class[] {})
.invoke(obj, new Object[] {});
}
/**
* @Author pengyunlong
* @Description 动态修改类,只能修改还未加载的类
* @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!
- 生成字节码到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 动态修改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是一款字节码编辑工具,同时也是一个动态类库,它可以直接检查、修改以及创建 Java类。
* 以下例子就是创建一个动态类
*
*/
public class CompilerByJavassist {
/**
* @Author pengyunlong
* @Description 动态修改类,只能修改还未加载的类
* @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!