简述
Javassist(Java Programming Assistant)是一个 操作Java 字节码的类库,主要用于在运行时或编译时修改 Java 类的字节码。
它提供了相对简单的 API,让开发者可以动态地创建、修改和加载 Java 类 , 从而实现诸如动态代理、AOP(面向切面编程)、代码生成等高级功能。
API学习
1)ClassPool
作用:ClassPool
作为一个类管理器,用于动态加载、操作 (对类的字段、方法进行增删改查操作) 和生成 Java 类的字节码。
相关API
① ClassPool.getDefault()
介绍:获取默认类池,采用单例模式,每次获取的类池都是同一个
② makeClass(String className)、
makeInterface(String className)、
makeAnnotation(String className) 等
介绍:在内存中创建类、接口、注解的字节码,返回的都是CtClass对象,此时还并未将类装载到JVM
③ get(String className) 、
getCtClass(String className)
介绍:获取内存中类的字节码对象
2)CtClass
作用:封装类的字节码信息(这里的类是统称,代表Java中的类、接口、注解等)
相关API
① void addField(CtField f)、
void addMethod(CtMethod m)、
void addConstructor(CtConstructor c)
添加字段、方法、构造器
② void addInterface(CtClass ctInterface)、
void setInterfaces(CtClass[] list)
继承一个接口、多个接口
③ void setSuperclass(CtClass clazz)
指定父类
④ Class<?> toClass()
将字节码类装载到JVM,并返回Class对象
⑤ void writeFile(String directoryName)
将类保存到文件
应用
导入maven依赖
<!-- javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.2-GA</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
创建如下的一个接口,一个类
package com.xxx;
public interface FunctionInterface {
void print(Object o);
}
package com.xxx;
public class FunctionImpl implements FunctionInterface{
public void print(Object o) {
System.out.println(o);
}
}
利用javassist生成类代码如下:
① 在内存中创建接口FunctionInterface的字节码,并加载到JVM
Class<?> makeFunctionInterface() throws Exception {
// 1.获取默认类池
ClassPool classPool = ClassPool.getDefault();
// 2.创建接口的内存字节码
CtClass functionInterfaceCt = classPool.makeInterface("com.xxx.FunctionInterface");
// 3.在内存中添加方法声明
// 3.1创建方法声明,并指定所属
CtMethod printMethodDeclare = CtMethod.make("void print(Object o);", functionInterfaceCt);
// 3.2添加方法声明
functionInterfaceCt.addMethod(printMethodDeclare);
// 4.将接口装载到虚拟机
Class<?> functionInterface = functionInterfaceCt.toClass();
return functionInterface;
}
② 在内存中创建类FunctionImpl的字节码,并加载到JVM
Class<?> makeFunctionImpl() throws Exception {
// 1.获取默认类池
ClassPool classPool = ClassPool.getDefault();
// 2.创建类的内存字节码
CtClass functionImplCt = classPool.makeClass("com.xxx.FunctionImpl");
// 3.继承接口
functionImplCt.addInterface(classPool.getCtClass("com.xxx.FunctionInterface"));
// 4.在内存中添加方法实现
// 4.1创建方法实现,并指定所属
CtMethod printMethodImpl = CtMethod.make("public void print(Object o){System.out.println(o);}", functionImplCt);
// 4.2添加方法实现
functionImplCt.addMethod(printMethodImpl);
// 5.将类装载到虚拟机
Class<?> functionImpl = functionImplCt.toClass();
return functionImpl;
}
③ 通过反射测试对象的生成以及print方法的调用
void testNewObject() throws Exception {
// 1.反射创建对象
Object functionImplObject = Class.forName("com.xxx.FunctionImpl").newInstance();
// 2.获取print方法
Method printMethod = functionImplObject.getClass().getMethod("print", Object.class);
// 3.执行方法
printMethod.invoke(functionImplObject, "hello, javassist!");
}
问题解决
java.lang.reflect.InaccessibleObjectException:如果JDK版本>8,那么添加如下JVM参数和环境变量参数:
JVM参数:--add-opens java.base/java.lang=ALL-UNNAMED
环境变量:--add-opens java.base/sun.net.util=ALL-UNNAMED