Javassist 是一个开源的分析、编辑和创建 Java 字节码的类库,被广泛用于程序性的类文件操作和运行时 AOP 框架,能动态改变类的结构,或者动态生成类。
关于javassist和反射
Javassist 不是通过反射来实现的,而是通过直接操作字节码来实现的。
在 Java 中,每个类都会被编译成一个或多个字节码文件(.class
文件)。这些字节码文件包含了 Java 虚拟机(JVM)需要执行类中的代码的所有信息。Javassist 可以读取这些字节码文件,修改它们,然后写回去,或者直接创建新的字节码文件。这使得 Javassist 能够在运行时动态地创建和修改类。
反射(Reflection)则是 Java 提供的一个特性,它允许程序在运行时获取类的信息(如类名、方法、字段等),并动态地调用方法、访问字段等。与 Javassist 不同,反射不能修改类的结构,只能动态地使用类。
所以,尽管 Javassist 和反射都能在运行时动态地操作类,但是它们的实现机制和能力是不同的。
下面是些使用javassist的小demo
public class JavaSsistDemo1 {
public static void main(String[] args) throws Exception {
// 获取类池
ClassPool pool = ClassPool.getDefault();
// 创建类
CtClass cc = pool.makeClass("com.example.GeneratedPOJO");
// 创建字段,字段的类型,字段名称
CtField fooField = new CtField(pool.get("java.lang.String"), "foo", cc);
//设置私有化
fooField.setModifiers(Modifier.PRIVATE);
//类中添加该字段
cc.addField(fooField);
//创建get方法
cc.addMethod(CtNewMethod.getter("getFoo", fooField));
//创建set方法
cc.addMethod(CtNewMethod.setter("setFoo", fooField));
CtMethod helloMethod = CtNewMethod.make(
"public void hello() { System.out.println(\"Hello Javassist!\"); }",
cc
);
cc.addMethod(helloMethod);
//可以选择写入磁盘,也可以不写入直接在内存中使用
cc.writeFile("J:\\javassist\\javassist-01\\target\\classes\\");
//获得类对象
Class<?> clazz = cc.toClass();
//这里用Class.forName("")也是可以的,就是没有必要
//Class<?> clazz1 = Class.forName("com.example.GeneratedPOJO");
//实例化
Object instance = clazz.newInstance();
Method getter = clazz.getMethod("getFoo");
System.out.println(getter.invoke(instance));
Method hello = clazz.getMethod("hello");
hello.invoke(instance);
//如果方法有返回值
String result = (String) hello.invoke(instance);
System.out.println(result); // prints "Hello Javassist!"
}
// public static void main(String[] args) throws Exception {
// // 获取类池
// ClassPool pool = ClassPool.getDefault();
// // 创建类
// CtClass ctClass = pool.makeClass("com.com.example.GeneratedPOJO");
// // 创建⽅法
// // 1.返回值类型 2.⽅法名 3.形式参数列表 4.所属类
// CtMethod ctMethod = new CtMethod(CtClass.voidType, "execute", new
// CtClass[]{}, ctClass);
// // 设置⽅法的修饰符列表
// ctMethod.setModifiers(Modifier.PUBLIC);
// // 设置⽅法体
// ctMethod.setBody("{System.out.println(\"hello world\");}");
// // 给类添加⽅法
// ctClass.addMethod(ctMethod);
// // 调⽤⽅法
// Class<?> aClass = ctClass.toClass();
// Object o = aClass.newInstance();
// Method method = aClass.getDeclaredMethod("execute");
// method.invoke(o);
// }
}
cc.toClass()
是动态创建并加载类,而 Class.forName()
是加载已经存在的类。
关于Mybatis的demo,我们当里面只有一个方法
public static void main(String[] args) throws CannotCompileException, IllegalAccessException, InstantiationException {
//模仿Mybatis动态创建实现类
//获取类池
ClassPool pool=ClassPool.getDefault();
//制造类
CtClass ctClass=pool.makeClass("com.example.UserDaoImpl");
//制造接口
//CtClass ctInterface=pool.makeClass("com.example.UserDao");
//注意javassits的类加载器和UserDao加载器可能不是同一个
//让类实现接口
//这里其实用CtClass ctInterface = pool.get("com.example.UserDao");更加合适
CtClass ctInterface = pool.get("com.example.UserDao");
ctClass.addInterface(ctInterface);//UserDaoImpl implements UserDao
//实现接口中的方法,这个比较麻烦,这里好比我们已经知道了所有方法什么的
//制造方法
CtMethod ctMethod=CtMethod.make("public void delete(){System.out.println(\"delete biubiubiu\");}",ctClass);
//将方法添加到类中
ctClass.addMethod(ctMethod);
//在内存中生成类,同时将生成的类加载到JVM当中
Class<?> aClass = ctClass.toClass();
UserDao userDao=(UserDao) aClass.newInstance();
System.out.println(userDao.getClass());
userDao.delete();
}
模拟实现Mapper接口所有方法
public static void main(String[] args) throws CannotCompileException, IllegalAccessException, InstantiationException, NotFoundException {
//模仿Mybatis动态创建实现类
//获取类池
ClassPool pool=ClassPool.getDefault();
//制造类
CtClass ctClass=pool.makeClass("com.example.UserDaoImpl");
//获取接口
CtClass ctInterface=pool.get("com.example.UserDao");
//让类实现接口
ctClass.addInterface(ctInterface);//UserDaoImpl implements UserDao
//实现接口中所有的方法
//获取接口中所有的方法
Method[] methods=UserDao.class.getDeclaredMethods();
//遍历中实现抽象方法,然后把方法放到了类中
Arrays.stream(methods).forEach(method->{
//method是接口中所有的抽象方法
//把method抽象方法给实现了
CtMethod ctMethod=null;
try {
StringBuilder methodCode=new StringBuilder();
methodCode.append("public ");//修饰符列表
methodCode.append(method.getReturnType().getName());//追加返回值类型
methodCode.append(" ");
methodCode.append(method.getName());//追加方法名
methodCode.append("(");
//也许方法有参数
Class<?>[] parameterTypes = method.getParameterTypes();
for(int i=0;i<parameterTypes.length;i++){
Class<?> parameterType = parameterTypes[i];
methodCode.append(parameterType.getName());//参数类型
methodCode.append(" ");
methodCode.append("arg"+i);//变量名随意,不重复就行
if(i!=parameterTypes.length-1){
methodCode.append(",");
}
}
methodCode.append("){System.out.println(1111);");
//动态添加return语句
String simpleName = method.getReturnType().getSimpleName();//返回值类型的简类名
if("void".equals(simpleName)){
}else if("int".equals(simpleName)){
methodCode.append("return 1;");
}else if("String".equals(simpleName)) {
methodCode.append("return \"hello\";");
}else if("Map".equals(simpleName)) {
methodCode.append("Map<String,String> map=new HashMap<>(); return map;");
}
methodCode.append("}");
System.out.println(methodCode);
//制造方法
ctMethod=CtMethod.make(methodCode.toString(),ctClass);
//将方法添加到类中
ctClass.addMethod(ctMethod);
} catch (Exception e) {
e.printStackTrace();
}
});
//在内存中生成类,同时将生成的类加载到JVM当中
Class<?> aClass = ctClass.toClass();
//创建对象
UserDao userDao=(UserDao) aClass.newInstance();
System.out.println(userDao.getClass());
//调用方法
userDao.delete();
userDao.selectasd("as");
}