第五篇 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表达式

对于继承关系,有些不支持

posted on 2018-06-03 02:31  奋斗的小刀001  阅读(390)  评论(0编辑  收藏  举报

导航