一、javassist
javassist让我们操作字节码更加简单,它是一个类库,允许我们修改字节码。它允许java程序动态的创建、修改类。
javassist提供了两个层次的API,基于源码级别的和字节码级别的。
二、javassist创建类
1.获取类池
2.在类池中添加要创建的类
3.添加变量,方法,构造器
4.将创建好的类写出
import java.io.IOException; import java.lang.String; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.NotFoundException; public class TestSsist { public static void main(String[] args) throws CannotCompileException, NotFoundException { //获取默认类池 ClassPool pool = ClassPool.getDefault(); //在类池中创建一个类 CtClass cc = pool.makeClass("com.TestSsist.TestUser");//包名(com.TestSsist)+类名(TestUser) //创建属性 CtField name = CtField.make("private String name;", cc);//(代码,类) CtField age = CtField.make("private int age;", cc); //添加属性 cc.addField(name); cc.addField(age); //创建方法 CtMethod setAll = CtMethod.make("public void setName(String name,int age){" + "this.name = name;" + "this.age = age;" + "}", cc); CtMethod getName = CtMethod.make("public String getName(){" + "return this.name;" + "}", cc); //添加方法 cc.addMethod(setAll); cc.addMethod(getName); //创建构造器 CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String"),CtClass.intType},cc); //设置构造器体内容,即{}部分内容 constructor.setBody("{this.name = name;" + "this.age = age;}"); //添加构造器 cc.addConstructor(constructor); try { cc.writeFile("F:\\TestJava");//将创建的类写出到指定路径 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("创建成功!"); } }
运行结果:
创建成功
我们可以看指定目录下有创建好的class文件。
字节码文件我们是无法直接查看的,我们可以通过工具(XJad)将其反编译进行查看我们创建好的类。
三、获取类基本信息(该类已存在)
测试类(TestUser)内容如下:(该类已存在)
import java.io.Serializable; public class TestUser implements Serializable{ private String naem; private int age; public TestUser() {} public TestUser(String naem, int age) { super(); this.naem = naem; this.age = age; } public String getNaem() { return naem; } public void setNaem(String naem) { this.naem = naem; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void testSsist(int a){ System.out.println("ssist:"+a); } }
public String getName();//获取类名
public CtClass getSuperclass();//获取父类
public CtClass[] getInterfaces();//获取接口
1.获取类池
2.获取指定类
3.获取类相关信息
import java.io.IOException; import java.lang.String; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.NotFoundException; public class TestSsist { public static void main(String[] args) throws CannotCompileException, NotFoundException { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("TestSsist.TestUser"); System.out.println("类名:" + cc.getName()); System.out.println("父类:"+cc.getSuperclass().getName()); for(CtClass temp:cc.getInterfaces()) System.out.println("接口:" + temp.getName()); } }
运行结果:
类名:TestSsist.TestUser
父类:java.lang.Object
接口:java.io.Serializable
四、动态添加方法
1.获取类池
2.获取对应类(TestUser)
3.动态添加方法
4.通过反射调用动态添加的方法
import java.io.IOException; import java.lang.String; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.NotFoundException; public class TestSsist { public static void main(String[] args) throws CannotCompileException, NotFoundException, NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException { ClassPool pool = ClassPool.getDefault();//获取类池 CtClass cc = pool.get("TestSsist.TestUser");//获取指定类 //public CtMethod(CtClass returnType, String mname,CtClass[] parameters, CtClass declaring) //动态添加方法 CtMethod mAdd = new CtMethod(CtClass.intType,"add", new CtClass[]{CtClass.intType,CtClass.intType},cc); mAdd.setModifiers(Modifier.PUBLIC);//设置方法访问权限 //因为我们在前面添加方法时只指定了形参类型,并没有指定形参名,所以要用如下方法设置。 //设置方法体,$1+$2,第一个参数+第二个参数:例如int add(int a,int b){...} retutn $1+$2就等于return a+b; mAdd.setBody("{System.out.println($1 +\"+\"+ $2);return $1+$2;}"); cc.addMethod(mAdd); //通过反射调用动态生成的方法 Class cl = cc.toClass(); Object u1 = cl.newInstance(); //获取指定方法 Method addm = cl.getDeclaredMethod("add",int.class,int.class); Object result = null; try { result = addm.invoke(u1,50,60);//调用方法 } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(result); } }
运行结果:
50+60
110
添加的方法只是运行时存在,运行结束就会消失,并不会将其真正写入TestUser类中,如果想保存的话,就通过writeFile写出。
五、动态修改方法
1.获取类池
2.获取指定类(TestUser)
3.获取指定类中指定方法
4.调用insertBefore,insertAfter、insertAt插入新语句,即修改方法。
5.通过反射调用动态修改后的方法
import java.io.IOException; import java.lang.String; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.NotFoundException; public class TestSsist { public static void main(String[] args) throws CannotCompileException, NotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException { ClassPool pool = ClassPool.getDefault();//获取类池 CtClass cc = pool.get("TestSsist.TestUser");//获取指定类 //获取指定方法,(方法名,方法类型(CtClass[]类型)) CtMethod m1 = cc.getDeclaredMethod("testSsist",new CtClass[]{CtClass.intType});//获取类中指定方法 //在32行插入语句 m1.insertAt(32, "System.out.println(\"32\");"); //在方法体开头插入语句 m1.insertBefore("System.out.println(\"start\");"); //在方法体末尾插入语句 m1.insertAfter("System.out.println(\"end\");"); //通过反射调用动态修改后的方法 Class cl = cc.toClass(); Object u1 = cl.newInstance(); //获取指定方法 Method addm = cl.getDeclaredMethod("testSsist",int.class); Object result = null; try { result = addm.invoke(u1,50);//调用方法 } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
运行结果:
start ssist:50 32 end
先执行方法体开头插入的语句
System.out.println(\"start\");
然后执行方法中自带的语句System.out.println("ssist:"+a);
然后执行插入到32行的语句Syste.out.println(32);
最后执行方法体末尾插入的语句System.out.println("end");
六、动态创建修改属性
1.创建属性
2.为对应属性创建set方法
3.通过反射调用set方法为其赋值
4.通过反射获取属性值
import java.io.IOException; import java.lang.String; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.NotFoundException; public class TestSsist { public static void main(String[] args) throws CannotCompileException, NotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, ClassNotFoundException { ClassPool pool = ClassPool.getDefault();//获取类池 CtClass cc = pool.get("TestSsist.TestUser");//获取指定类 //动态创建password属性 CtField nameF = CtField.make("private String password;", cc); cc.addField(nameF); //动态创建set方法 CtMethod set = CtMethod.make("public void setPassword(String password){" + "this.password = password;" + "}", cc); cc.addMethod(set); Class clz = cc.toClass(); Object test = clz.newInstance(); try { //通过反射调用set方法,为password设置值 Method setV = clz.getDeclaredMethod("setPassword",new Class[]{String.class}); setV.invoke(test, "123456"); //获取其属性值 Field pw = clz.getDeclaredField("password"); pw.setAccessible(true);//password是私有属性,设置不做访问检查 System.out.println(pw.get(test)); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
运行结果:
123456
本例所用javassist下载地址及版本:https://github.com/jboss-javassist/javassist/releases,Javassist 3.21.0-GA