概述Javassist是一个用于分析、编辑和创建Java字节码的开源类库,可以直接编辑生成Java生成的字节码。与bcel、asm等工具相比,开发者可以在不知道虚拟机指令的情况下动态改变类结构或动态生成类。javassist易于使用且速度很快。重要类1.ClassPool:javassist的类池,使用ClassPool类来跟踪和控制正在操作的类,其工作方式与JVM类加载器非常相似2.CtClass:CtClass提供类操作,比如在类中动态添加新字段、方法和构造函数,以及更改类、超类和接口的方法。3.CtField:类的属性,通过它可以为类创建新的属性,也可以修改已有属性的类型,访问修饰符等。这里我搭建了一个前端学习交流按钮组:132667127,本人整理最新的前端资料和进阶开发教程。如果愿意,可以进群一起学习交流。4、CtMethod:类中的方法,通过它可以为类创建新的方法,还可以修改返回类型和访问修饰符等,甚至可以修改方法体内容的代码5、CtConstructor:类似于CtMethod的API使用ClassPool1//类库,class2在jvm中加载ClassPoolpool=ClassPool.getDefault();3//加载一个已知的类,注意:参数必须是完整的类名4CtClassctClass=pool.get("com.iheima.Student");5//创建一个新类,类名必须是完整的classname6CtClasstClass=pool.makeClass("com.iheima.Calculator");CtField1//获取已知类2的属性CtFieldctField=ctClass.getDeclaredField("name");3//构建新类的成员变量4CtFieldctFieldNew=newCtField(CtClass.intType,"age",ctClass);5//设置类的访问修饰符为public6ctFieldNew.setModifiers(Modifier.PUBLIC);7//给class8添加属性ctClass.addField(ctFieldNew);CtMethod1//获取现有方法4//新建一个方法,参数1:方法的返回类型,参数2:名称,参数3:参数方法,参数4:方法所属的类5CtMethodctMethod=newCtMethod(CtClass.intType,"calc",newCtClass[]{CtClass.intType,CtClass.intType},tClass);6//设定方法访问修改7ctMethod.setModifiers(Modifier.PUBLIC);8//将新方法添加到类9ctClass.addMethod(ctMethod);10//方法体内容代码$1代表第一个参数,$2代表第二个参数11ctMethod.setBody("return$1+$2;");2CtMethodctMethod=ctClass.getDeclaredMethod("sayHello");CtConstructor//获取已有构造方法,参数为构造方法CtConstructor的参数类型数组]{CtClass.intType},ctClass);ctConstructor.setModifiers(Modifier.PUBLIC);ctConstructor.setBody("this.age=$1;");ctClass.addConstructor(ctConstructor);//直接创建ctConstructor=CtNewConstructor.make("publicStudent(intage){this.年龄=年龄;}",ctClass);案例创建maven项目并添加依赖添加依赖添加Student类包com.iheima;publicclassStudent{privateintage;privateStringname;publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=名称e;}publicvoidsayHello(){System.out.println("Hello,Mynameis"+this.name);}}修改现有方法体,在现有学生类的sayHello方法中插入新代码,当调用,控制台会输出:你好,我叫张三(name=ZhangSan)需求:通过动态修改sayHello方法,调用sayHello时,除了输出已有的内容外,还输出当前学生的年龄信息Create一个JavassistDemo测试类,代码实现如下:packagecom.iheima.test;导入org.junit.Test;导入com.iheima.Student;导入javassist.ClassPool;导入javassist.CtClass;importjavassist.CtMethod;@SuppressWarnings("unchecked")publicclassJavassistDemo{@Testpublicvoidt1()throwsException{//类库池,类加载到jvmClassPoolpool=ClassPool.getDefault();//获取指定的Student类CtClassctClass=pool.get("com.iheima.Student");//获取sayHello方法CtMethodctMethod=ctClass.getDeclaredMethod("sayHello");//在方法代码后添加一段代码ctMethod.insertAfter("System.out.println(\"I'm\"+this.age+\"yearsold.\");");//使用当前的ClassLoader加载修改后的类ClassnewClass=ctClass.toClass();学生stu=newClass。新实例();stu.setName("张三");stu.setAge(18);stu.sayHello();}}运行结果:动态添加方法接下来我们在Student类中添加一个计算方法,但不是直接在Student类中添加,而是使用javassist动态添加1publicintcalculate(inta,intb){2returna+b;3}创建一个测试方法t2,代码如下1@Test2publicvoidt2()throwsException{3//类库池,jvm中加载的class4ClassPoolpool=ClassPool.getDefault();5//获取指定的Student类6CtClassctClass=pool.get("com.iheima.Student");7//创建一个calc方法,有两个参数,都是int类型8CtMethodctMethod=newCtMethod(CtClass.intType,"calc",9newCtClass[]{CtClass.intType,CtClass.intType},ctClass);10//设置方法的访问修改11ctMethod.setModifiers(Modifier.PUBLIC);12//设置方法体代码13ctMethod.setBody("return$1+$2;");14//将新方法添加到原来的类中15ctClass.addMethod(ctMethod);16//加载修改后的类17ctClass.toClass();18//创建对象19Studentstu=newStudent();20//获取calc方法21MethoddMethod=Student.class.getDeclaredMethod("calc",newClass[]{int.class,int.class});22//反射调用方法23Objectresult=dMethod.invoke(stu,10,20);24//打印结果25System.out.println(String.format("调用calc方法,传入参数:%d,%d",10,20));26System.out.println("返回结果:"+(int)result);27}控制台输出结果:动态创建类接下来我们来变个魔术,无中生有1@Test2publicvoidt3()throwsException{3ClassPoolpool=ClassPool.getDefault();4//创建教师班级5CtClassteacherClass=pool.makeClass("com.iheima.Teacher");6//设置为公共班级7teacherClass.setModifiers(Modifier.PUBLIC);8//获取字符串类型9CtClassstringClass=pool.get("java.lang.String");10//获取列表类型11CtClasslistClass=pool.get("java.util.List");12//获取学生类型13CtClassstudentClass=pool.get("com.itheima.Student");14//给teacher添加name属性15CtFieldnameField=newCtField(stringClass,"name",teacherClass);16nameField.setModifiers(Modifier.PUBLIC);17teacherClass.addField(nameField);18//给教师类添加学生属性19CtFieldstudentList=newCtField(listClass,"students",teacherClass);20studentList.setModifiers(Modifier.PUBLIC);21teacherClass.addField(studentList);第2223章24CtConstructorctConstructor=CtNewConstructor.make("公共教师(){this.name=\"abc\";this.students=newjava.util.ArrayList();}",teacherClass);25teacherClass.addConstructor(ctConstructor);第2627章29m.setModifiers(修饰符.PUBLIC);30//给students属性添加Student对象,$1代表参数131m.setBody("this.students.add($1);");32teacherClass.addMethod(m);3334//在教师类中添加sayHello方法35m=newCtMethod(CtClass.voidType,"sayHello",newCtClass[]{},teacherClass);36m.setModifiers(Modifier.PUBLIC);37m.setBody("System.out.println(\"你好,我的名字是\"+this.name);");38m.insertAfter("System.out.println(\"我有\"+this.students.size()+\"学生\");");39teacherClass.addMethod(m);4041//加载修改后的class42Class>cls=teacherClass.toClass();43//实例教师对象44Objectobj=cls.newInstance();45//获取addStudent方法46Methodmethod=cls.getDeclaredMethod("addStudent",Student.class);运行结果:总结struts2和hibernate中都使用了javassist,都是用来动态修改字节码的。一般开发中不会用到,但封装它工作的框架时会更好。javassist虽然提供了一套简单易用的API,但是如果用于普通开发,会有以下缺点:1.必须通过ClassPool获取引用的类型,然后才能使用。2.代码块中使用的引用类型,使用时一定要写完整的类名。3、即使代码块内容写错了,也不会像eclipse等开发工具那样提示。它只会在运行时报告错误。4.动态修改的类,修改前jvm中必须没有该类的实例对象。修改后的方法的实现必须发生在加载修改后的类之前。