javassist简介
Javassist是⼀个开源的分析、编辑和创建Java字节码的类库。是由东京⼯业⼤学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加⼊了开放源代码JBoss 应⽤服务器项⽬,通过使⽤
Javassist对字节码操作为JBoss实现动态"AOP"框架。
Javassist是可以动态编辑Java字节码的类库。它可以在Java程序运行时定义一个新的类,并加载到JVM中;还可以在JVM加载时修改一个类文件。Javassist不必关心字节码相关的规范也是可以编辑类文件的。
javassist使用
在Javassist中每个需要编辑的class都对应一个CtCLass实例,CtClass的含义是编译时的类(compile time class),这些类会存储在Class Pool中(Class poll是一个存储CtClass对象的容器)。
环境配置
为了减少演示的复杂度,我们都在Maven项目下进行,因为我们可以直接引入依赖就可以达到我们导包的目的,很方便,不用再去下载jar包,然后自己手动导入了。
导入依赖:版本号可以使用别的
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.1-GA</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
使用javassist创建方法
步骤
1.获取类池
2.制造类
3.制造方法
4.将方法添加到类中
5.在内存中生成类,同时生成加载到JVM中
测试的时候可以使用反射机制创建对象
@Test
public void testGenerateFirstClass() throws Exception {
//1.获取类池
ClassPool pool = ClassPool.getDefault();
//2.制造类
CtClass ctClass = pool.makeClass("com.javassist.bank.dao.impl.AccountDaoImpl");
//3.制造方法
CtMethod ctMethod = CtMethod.make("public void insert(){System.out.println(123);}", ctClass);
//4.将方法添加到类中
ctClass.addMethod(ctMethod);
//5.在内存中生成类,同时生成加载到JVM中
ctClass.toClass();
//反射机制创建对象,调方法
Class<?> clazz = Class.forName("com.javassist.bank.dao.impl.AccountDaoImpl");
//创建对象
Object o = clazz.newInstance();
Method insertMethod = clazz.getDeclaredMethod("insert");
insertMethod.invoke(o);
}
注意:运行的时候会出现InaccessibleObjectException,原因是jdk8版本之后有了java.base这个包,jdk8版本之前没有,导致找不到。
需要加这两个参数
–add-opens java.base/java.lang=ALL-UNNAMED
–add-opens java.base/sun.net.util=ALL-UNNAMED
注意配置完后是点击这个绿色小箭头运行,点击别的会新建一个junit
运行结果
使用javassist和已有接口创建实现方法
创建一个接口
public interface AccountDao {
void delete();
}
步骤
1.获取类池
2.制造类
3.制造接口
4.添加接口到类中(实现接口)
5.制造方法
6.实现接口里面的方法,将方法添加到类中
7.在内存中生成类,同时生成加载到JVM中。
@Test
public void testGenerateImpl() throws Exception {
//1.获取类池
ClassPool pool = ClassPool.getDefault();
//2.制造类
CtClass ctClass = pool.makeClass("com.javassist.bank.dao.impl.AccountDaoImpl");
//3.制造接口
CtClass ctInterface = pool.makeInterface("com.javassist.bank.dao.AccountDao");
//4.添加接口到类中(实现接口)
ctClass.addInterface(ctInterface);//相当于AccountDaoImpl implements AccountDao
//5.制造方法
CtMethod ctMethod = CtMethod.make("public void delete(){System.out.println(\"Hello detele\");}", ctClass);
//6.实现接口里面的方法,将方法添加到类中
ctClass.addMethod(ctMethod);
//7.在内存中生成类,同时生成加载到JVM中,这里会返回一个class对象
Class<?> clazz = ctClass.toClass();
//8.创建对象并调用
AccountDao accountDao = ((AccountDao) clazz.newInstance());
accountDao.delete();
}
运行结果
动态创建实现方法
以上是知道接口有哪个方法,而javassist是可以进行动态创建的,我们需要进行动态创建实现方法
接口:
public interface AccountDao {
void delete();
int insert(String actno);
int update(String actno,Double balance);
String selectByActno(String actno);
}
步骤
- 1.获取类池
- 2.制造类
- 3.制造接口
- 4.添加接口到类中(实现接口)
- 5、获取接口中所有的方法
- 6.通过遍历循环动态制造方法,并将方法添加到类中
- 6.1通过StringBuffer动态制造方法
- 6.2通过循环遍历动态获取参数类型
- 6.3动态添加return语句
- 7.在内存中生成类,同时生成加载到JVM中
@Test
public void testGenerateAccountDaoImpl() throws Exception {
//1.获取类池
ClassPool pool = ClassPool.getDefault();
//2.制造类
CtClass ctClass = pool.makeClass("com.javassist.bank.dao.impl.AccountDaoImpl");
//3.制造接口
CtClass ctInterface = pool.makeInterface("com.javassist.bank.dao.AccountDao");
//4.添加接口到类中(实现接口)
ctClass.addInterface(ctInterface);//相当于AccountDaoImpl implements AccountDao
//5、获取接口中所有的方法
Method[] methods = AccountDao.class.getDeclaredMethods();
//6.通过遍历循环动态制造方法,并将方法添加到类中
Arrays.stream(methods).forEach(method -> {
try {
//6.1通过StringBuffer动态制造方法
StringBuffer methodCode = new StringBuffer();
methodCode.append("public ");//接口一定是public
methodCode.append(method.getReturnType().getName() + " ");//追加返回值类型
methodCode.append(method.getName());//追加方法名
methodCode.append("(");
//6.2通过循环遍历动态获取参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
methodCode.append(parameterType.getName() + " ");//追加参数类型
methodCode.append("arg" + i);//追加参数名
if (i != parameterTypes.length-1) {
methodCode.append(",");
}
}
methodCode.append(")");
methodCode.append("{");
methodCode.append("System.out.println(321);");
//6.3动态添加return语句
String returnSimpleName = method.getReturnType().getSimpleName();//获取返回值类型的简称,因为getReturnType()是带包名的
if ("void".equals(returnSimpleName)) {
}else if ("int".equals(returnSimpleName)){
methodCode.append("return 1;");
}else if ("String".equals(returnSimpleName)){
methodCode.append("return \"Hello\";");
}
methodCode.append("}");
System.out.println(methodCode);
CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
ctClass.addMethod(ctMethod);
} catch (Exception e) {
e.printStackTrace();
}
});
//7.在内存中生成类,同时生成加载到JVM中,这里会返回一个class对象
Class<?> clazz = ctClass.toClass();
//8.创建对象并调用
AccountDao accountDao = ((AccountDao) clazz.newInstance());
accountDao.insert("aaa");
accountDao.delete();
accountDao.selectByActno("aaaa");
accountDao.update("aaa",20.6);
}
运行结果