<dependency> <groupId>org.javassistgroupId> <artifactId>javassistartifactId> <version>3.25.0-GAversion>dependency>
1. 创建类对象
为了演示方便,我们先来创建一个全新的 .class类对象。
@Test public void test() { try { ClassPool pool = ClassPool.getDefault(); // 1. 创建一个空类 CtClass cc = pool.makeClass("com.github.jimbean0615.test.Person"); // 2. 新增一个字段 private String name; // 字段名为name CtField param = new CtField(pool.get("java.lang.String"), "name", cc); // 访问级别是 private param.setModifiers(Modifier.PRIVATE); // 初始值是zhangsan cc.addField(param, CtField.Initializer.constant("zhangsan")); // 3. 生成 getter/setter 方法 cc.addMethod(CtNewMethod.setter("setName", param)); cc.addMethod(CtNewMethod.getter("getName", param)); // 4. 添加无参的构造函数 CtConstructor cons = new CtConstructor(new CtClass[]{}, cc); cons.setBody("{name = \"zhangsan\";}"); cc.addConstructor(cons); // 5. 添加有参的构造函数 cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc); // $0代表this // $1,$2,$3... 依次代表第几个方法参数 // $$表示所有参数 cons.setBody("{$0.name = $1;}"); cc.addConstructor(cons); // 6. 创建一个名为printName方法,无参数,无返回值,输出name值 CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc); ctMethod.setModifiers(Modifier.PUBLIC); ctMethod.setBody("{System.out.println(name);}"); cc.addMethod(ctMethod); //这里会将这个创建的类对象编译为.class文件 cc.writeFile("/Users/zhangjianbin/Downloads/java"); } catch (Exception e) { e.printStackTrace(); } }
不出意外的话,我们到
/Users/zhangjianbin/Downloads/java/com/github/jimbean0615/test
目录下会看到新创建的Person.class文件。
使用idea可以打开该class文件,看看内容是否符合预期。
不出所料,class文件内容正是我们想要的。 2. 调用类方法
接下来我们开始尝试调用以上class文件的方法。
@Test public void test1() { try { ClassPool pool = ClassPool.getDefault(); pool.appendClassPath("/Users/zhangjianbin/Downloads/java"); // 加载class文件 CtClass ctClass = pool.get("com.github.jimbean0615.test.Person"); // 创建类实例 Object person = ctClass.toClass().newInstance(); // 设置值 Method setName = person.getClass().getMethod("setName", String.class); setName.invoke(person, "lisi"); // 输出值 Method execute = person.getClass().getMethod("printName"); execute.invoke(person); } catch (Exception e) { e.printStackTrace(); } }
为了调用方法方便,我们还可以给Person.class类设置一个接口。
@Test public void test2() throws Exception { ClassPool pool = ClassPool.getDefault(); pool.appendClassPath("/Users/zhangjianbin/Downloads/java/"); pool.appendClassPath(new ClassClassPath(this.getClass())); // 获取接口 CtClass codeClassI = pool.get("com.github.jimbean0615.test.PersonI"); // 获取上面生成的类 CtClass ctClass = pool.get("com.github.jimbean0615.test.Person"); // 使代码生成的类,实现 PersonI 接口 ctClass.setInterfaces(new CtClass[]{codeClassI}); // 以下通过接口直接调用 强转 PersonI person = (PersonI) ctClass.toClass().newInstance(); System.out.println(person.getName()); person.setName("王二麻子"); person.printName(); }
/** * @author zhangjb */public interface PersonI { void setName(String name); String getName(); void printName();}
3. 修改现有类方法
一般实际工作中遇到的场景更多的是修改已有的类。比如常见的日志切面。利用Javassist来实现类似的功能。
@Test public void test3() throws Exception { ClassPool pool = ClassPool.getDefault(); pool.appendClassPath("/Users/zhangjianbin/Downloads/java/"); pool.appendClassPath(new ClassClassPath(this.getClass())); // 获取生成的类 CtClass ctClass = pool.get("com.github.jimbean0615.test.Person"); CtMethod ctmethod = ctClass.getDeclaredMethod("printName"); ctmethod.insertBefore("{ $0.name = \"王二麻子\"; }"); // 以下通过接口直接调用 强转 Object person = ctClass.toClass().newInstance(); Method printNameMethod = person.getClass().getMethod("printName"); printNameMethod.invoke(person); }
4. 新增一个方法
@Test public void test4() throws Exception { ClassPool pool = ClassPool.getDefault(); pool.appendClassPath("/Users/zhangjianbin/Downloads/java/"); pool.appendClassPath(new ClassClassPath(this.getClass())); // 获取生成的类 CtClass ctClass = pool.get("com.github.jimbean0615.test.Person"); //新增一个方法 CtMethod ctMethod = new CtMethod(CtClass.voidType, "setPersonName", new CtClass[]{pool.get("java.lang.String")}, ctClass); ctMethod.setModifiers(Modifier.PUBLIC); ctMethod.setBody("{ System.out.println(\"the name after modified is \" + $1);}"); ctClass.addMethod(ctMethod); // 以下通过接口直接调用 强转 Object person = ctClass.toClass().newInstance(); Method setPersonNameMethod = person.getClass().getMethod("setPersonName", String.class); setPersonNameMethod.invoke(person, "王二麻子"); }
今天我们主要测试的是使用Javassist进行方法Method相关操作,对于属性Field的操作类似,这里就不做演示了。 附录:
Javassist 以
$
开头的几个标识符的含义:
符号 | 含义 |
|
|
| 方法参数数组.它的类型为 |
| 所有实参。例如, |
|
|
| 返回结果的类型,用于强制类型转换 |
| 包装器类型,用于强制类型转换 |
| 返回值 |
| 类型为 java.lang.Class 的参数类型数组 |
| 一个 java.lang.Class 对象,表示返回值类型 |
| 一个 java.lang.Class 对象,表示当前正在修改的类 |