第五篇 java字节码操作
java动态性的两种常见的实现方式:
①字节码操作
②反射
运行时操作字节码可以让我们实现如下功能:
①动态生成新的类
②动态改变某个类的结构(添加、删除、修改 新的属性或方法)
优点:
比反射的开销小,性能高
Javassist性能高于反射,低于ASM
BCEL(Byte Code Engineering Library):
是java classworking 广泛使用的一种框架,它可以让你深入JVM汇编语言进行类操作的细节。BCEL与Javassist有不同的处理字节码的方法,BCEL在实际的JVM指令层次上进行操作(BCEL拥有丰富的JVM指令级支持)而javassist所强调的是源代码级别的工作。
ASM:
是一个轻量级java字节码操作框架,直接涉及到JVM底层的操作和指令。
CGLIB(Code Generation Library):
是一个强大的,高性能,高质量的code生成类库,基于ASM实现。
javassist:
是一个开源的分析、编辑和创建java字节码的类库。性能比ASM差,与cglib差不多,但是使用简单,很多开源框架都在使用。
导入javassist.jar包:
1 /** 2 * 3 * 测试使用javassist生成一个新的类 4 * 5 * @author Zhang XiaoDao 6 * 7 */ 8 public class Demo01 { 9 public static void main(String[] args) throws Exception { 10 //获取默认的类池 11 ClassPool pool = ClassPool.getDefault(); 12 //需要创建的类 13 CtClass cc = pool.makeClass("com.zzp.bean.Emp"); 14 15 //创建属性,也可以给属性赋值 16 CtField f1 = CtField.make("private int empno", cc); 17 CtField f2 = CtField.make("private String ename", cc); 18 cc.addField(f1); 19 cc.addField(f2); 20 21 //创建相关的方法 22 //创建set/get方法 23 CtMethod.make("public int getEmpno(){return empno;}", cc); 24 CtMethod.make("public void setEmpno(int empno){this.empno = empno;}", cc); 25 26 CtMethod m1 = CtMethod.make("public String getEname(){return ename;}", cc); 27 CtMethod m2 = CtMethod.make("public void setEname(String ename){this.ename = ename;}", cc); 28 cc.addMethod(m1); 29 cc.addMethod(m2); 30 31 //添加构造器 32 CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String") 33 },cc); 34 //设置构造器的方法体,method也能设置方法体 35 constructor.setBody("{this.empno=empno; this.ename=ename;}"); 36 cc.addConstructor(constructor); 37 38 //将上面写好的类生成到桌面 39 cc.writeFile("c:/desktop"); 40 System.out.println("生成类成功"); 41 } 42 }
创建注解类
1 public @interface Author { 2 String name(); 3 int year(); 4 }
创建实体类
1 @Author(name="Chiba",year=2005) 2 public class Emp { 3 4 private int empno; 5 private String ename; 6 7 public void sayHello(int a){ 8 System.out.println("sayHello,"+a); 9 } 10 11 public int getEmpno() { 12 return empno; 13 } 14 public void setEmpno(int empno) { 15 this.empno = empno; 16 } 17 public String getEname() { 18 return ename; 19 } 20 public void setEname(String ename) { 21 this.ename = ename; 22 } 23 public Emp(int empno, String ename) { 24 super(); 25 this.empno = empno; 26 this.ename = ename; 27 } 28 public Emp() { 29 super(); 30 // TODO Auto-generated constructor stub 31 } 32 }
创建测试类
1 /** 2 * 3 * 测试javassist的API 4 * 5 * @author Zhang XiaoDao 6 * 7 */ 8 public class Demo02 { 9 /** 10 * 处理类的基本用法 11 * @throws CannotCompileException 12 * @throws IOException 13 */ 14 public static void test01() throws Exception{ 15 ClassPool pool = ClassPool.getDefault(); 16 try { 17 CtClass cc = pool.get("com.zzp.Emp"); 18 //转化成字节码 19 byte[] bytecode = cc.toBytecode(); 20 System.out.println(Arrays.toString(bytecode)); 21 22 //获取全路径类名 23 System.out.println(cc.getName()); 24 //获取简要类名 25 System.out.println(cc.getSimpleName()); 26 //获取父类 27 System.out.println(cc.getSuperclass()); 28 //获取接口 29 System.out.println(cc.getInterfaces()); 30 } catch (NotFoundException e) { 31 // TODO Auto-generated catch block 32 e.printStackTrace(); 33 } 34 } 35 36 /** 37 * 38 * 测试产生新的方法 39 * @throws Exception 40 */ 41 public static void test02() throws Exception{ 42 ClassPool pool = ClassPool.getDefault(); 43 CtClass cc = pool.get("com.zzp.Emp"); 44 45 //CtMethod m = CtNewMethod.make("public int add(int a,int b){return a+b;}", cc); 46 //返回值参数,方法名称,参数类型,类 47 CtMethod m = new CtMethod(CtClass.intType, "add", new CtClass[]{CtClass.intType,CtClass.intType},cc); 48 //添加修饰符 49 m.setModifiers(Modifier.PUBLIC); 50 //添加方法体 注意用$指定参数 51 m.setBody("{return $1+$2;}"); 52 cc.addMethod(m); 53 54 //通过反射调用新生成的方法 55 Class clazz = cc.toClass(); 56 //获取对象 57 Object obj = clazz.newInstance(); 58 //获取对象的方法 59 Method method = clazz.getDeclaredMethod("add",int.class,int.class); 60 Object rs = method.invoke(obj, 200,300); 61 System.out.println(rs); 62 } 63 64 /** 65 * 66 * 修改已有的方法的方法体中的内容 67 * @throws Exception 68 */ 69 public static void test03() throws Exception{ 70 ClassPool pool = ClassPool.getDefault(); 71 CtClass cc = pool.get("com.zzp.Emp"); 72 //获取已有的方法 73 CtMethod m = cc.getDeclaredMethod("sayHello",new CtClass[]{CtClass.intType}); 74 //在方法执行前的前面 75 m.insertBefore("System.out.println($1);System.out.println(\"开始!!!\");"); 76 //在方法执行完之后 77 //m.insertAfter("System.out.println($1);System.out.println(\"结束!!!\");"); 78 79 //在第几行前面加代码 80 m.insertAt(11, "int b=11;System.out.println(\"b:\"+b);"); 81 82 //通过反射调用新生成的方法 83 Class clazz = cc.toClass(); 84 //获取对象 85 Object obj = clazz.newInstance(); 86 //获取对象的方法 87 Method method = clazz.getDeclaredMethod("sayHello",int.class); 88 Object rs = method.invoke(obj, 200); 89 System.out.println(rs); 90 } 91 92 /** 93 * 增加属性 94 * @param args 95 * @throws Exception 96 * @throws Exception 97 */ 98 public static void test04() throws Exception{ 99 ClassPool pool = ClassPool.getDefault(); 100 CtClass cc = pool.get("com.zzp.Emp"); 101 102 // CtField f1 = CtField.make("private int empno", cc); 103 //添加属性 104 CtField f1 = new CtField(CtClass.intType,"salary",cc); 105 f1.setModifiers(Modifier.PRIVATE); 106 //可以传默认值,也可以不传默认值 107 cc.addField(f1); 108 //添加set/get方法 109 cc.addMethod(CtNewMethod.getter("getSalary", f1)); 110 cc.addMethod(CtNewMethod.setter("setSalary", f1)); 111 } 112 113 /** 114 * 操作构造器 115 * @throws Exception 116 */ 117 public static void test05() throws Exception{ 118 ClassPool pool = ClassPool.getDefault(); 119 CtClass cc = pool.get("com.zzp.Emp"); 120 //获取所有的构造器 121 CtConstructor[] cs = cc.getConstructors(); 122 for (CtConstructor c : cs) { 123 System.out.println(c.getName()+"==="+c); 124 } 125 } 126 127 /** 128 * 129 * 获取注解 130 * @throws Exception 131 */ 132 public static void test06() throws Exception{ 133 ClassPool pool = ClassPool.getDefault(); 134 CtClass cc = pool.get("com.zzp.Emp"); 135 //获取所有的注解 136 Object[] all = cc.getAnnotations(); 137 Author a = (Author) all[0]; 138 String name = a.name(); 139 int year = a.year(); 140 System.out.println("name:"+name+"===="+"year:"+year); 141 } 142 143 public static void main(String[] args) throws Exception { 144 /*test01();*/ 145 /*test02();*/ 146 /*test03();*/ 147 /*test05();*/ 148 test06(); 149 } 150 }
局限性:
不支持注解的修改,但能通过底层的javassist来解决
不支持数组的初始化,除非只有数组的容量为1
不支持内部类和匿名类
不支持continue和break表达式
对于继承关系,有些不支持