一、字节码操作
1.Java动态性的两种常见实现方式:
- 字节码操作
- 反射
2.运行时操作字节码可以实现如下功能:
- 动态生成新的类
- 动态改变某个类的结构(添加/删除/修改 新的属性/方法)
3.优势:
- 比反射开销小,性能高
- Javaasist性能高于反射,低于ASM
二、常见的字节码操作类库
1.BCEL
Byte Code Engineering Library(BCEL),这是Apache Software Foundation的Jakarta项目的一部分。BCEL是Java classworking 广泛使用的一种框架,它可以让您深入jvm汇编语言进行类库操作的细节。BCEL与javassist有不同的处理字节码方法,BCEL在实际的jvm指令层次上进行操作(BCEL拥有丰富的jvm指令集支持) 而javassist所强调的是源代码级别的工作。2.ASM
是一个轻量级Java字节码操作框架,直接涉及到JVM底层的操作和指令高性能,高质量
3.CGLB(code generation library)
生成类库,基于ASM实现4.javassist
是一个开源的分析,编辑和创建Java字节码的类库。性能较ASM差,跟cglib差不多,但是使用简单。很多开源框架都在使用它。主页: http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/
三、javassist库的API详解
- javassist的最外层的API和java的反射包中的API及其类似
- 它主要有CtClass, CtMethod,CtField几个类组成,用于执行和JDK反射API中java.lang.Class, java.lang,reflect.Method,java.lang.reflect.Method.Field相同的操作
(1):创建一个类
代码:
public class JavassistTest {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("bean.User");
//创建属性
CtField field01 = CtField.make("private int id;",cc);
CtField field02 = CtField.make("private String name;", cc);
cc.addField(field01);
cc.addField(field02);
//创建方法
CtMethod method01 = CtMethod.make("public String getName(){return name;}", cc);
CtMethod method02 = CtMethod.make("public void setName(String name){this.name = name;}", cc);
cc.addMethod(method01);
cc.addMethod(method02);
//添加有参构造器
CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")},cc);
constructor.setBody("{this.id=id;this.name=name;}");
cc.addConstructor(constructor);
//无参构造器
CtConstructor cons = new CtConstructor(null,cc);
cons.setBody("{}");
cc.addConstructor(cons);
cc.writeFile("E:/workspace/TestCompiler/src");
}
}
注意:无参构造器和有参构造器
(2):方法操作:
- 修改已有的方法体(插入代码到已有方法体)
- 新增方法
- 删除方法
$0 $1 $2 | $0代表是this, $1代表方法参数的第一个参数,$2代表方法参数的第二个参数,以此类推,$N代表方法参数的第N个 |
$args | The type of $args is OBject[]. $args(0)对应的是$1,不是$0 |
$$ | 一个方法调用的深度 |
$r | 方法返回值的类型 |
$_ | 方法返回值。(修改方法体时不支持) |
addCatch() | 方法中加入try catch块 $e代表 异常对象 |
$class | this的类型(Class)。也就是$0的类型 |
$sig | 方法参数的类型(Class)数组,数组的顺序。 |
(3):属性操作
(4):构造方法操作
getConstructors()(5)注解操作
public @interface Author{
String name();int year();
}
@Author(name="over",year=2012)
public class Point{int x,int y;}
CtClass cc=ClassPool.getDefault().get("Point");
Object[] all = cc.getAnnotations();
Author a =(Author)all[0];
String name = a.name();
int year = a.year();
System.out.println(name+":"+year);
四、局限性:
- JDK5.0新语法不支持(包括泛型,枚举),不支持注解修改,但可以通过底层的javassist类来解决,具体参考:javassist,bytecode.annotation
- 不支持数组的初始化,如String[]{"a","b"},除非只有数组的容量为1
- 不支持内部类和匿名类
- 不支持continue和break 表达式
- 对于继承关系,有些不支持 。例如
class A{}
class B extends A{}
Class C extends B{}
查资料: javassist与反射的性能比较
Demo:
public class Demo01 {
//获取类的简单信息
public static void test01() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("bean.User");
//得到字节码
byte[] bytes = cc.toBytecode();
System.out.println(Arrays.toString(bytes));
System.out.println(cc.getName());//获取类名
System.out.println(cc.getSimpleName());//获取简要类名
System.out.println(cc.getSuperclass());//获取父类
System.out.println(cc.getInterfaces());//获取接口
System.out.println(cc.getMethods());//获取
}
//新生成一个方法
public static void test02() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("bean.User");
//第一种
//CtMethod cm = CtMethod.make("public String getName(){return name;}", cc);
//第二种
//参数:返回值类型,方法名,参数,对象
CtMethod cm = new CtMethod(CtClass.intType,"add",new CtClass[]{CtClass.intType,CtClass.intType},cc);
cm.setModifiers(Modifier.PUBLIC);//访问范围
cm.setBody("{return $1+$2;}");
//cc.removeMethod(m) 删除一个方法
cc.addMethod(cm);
//通过反射调用方法
Class clazz = cc.toClass();
Object obj = clazz.newInstance();//通过调用无参构造器,生成新的对象
Method m = clazz.getDeclaredMethod("add", int.class,int.class);
Object result = m.invoke(obj, 2,3);
System.out.println(result);
}
//修改已有的方法
public static void test03() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("bean.User");
CtMethod cm = cc.getDeclaredMethod("hello",new CtClass[]{pool.get("java.lang.String")});
cm.insertBefore("System.out.println(\"调用前\");");//调用前
cm.insertAt(29, "System.out.println(\"29\");");//行号
cm.insertAfter("System.out.println(\"调用后\");");//调用后
//通过反射调用方法
Class clazz = cc.toClass();
Object obj = clazz.newInstance();
Method m = clazz.getDeclaredMethod("hello", String.class);
Object result = m.invoke(obj, "张三");
System.out.println(result);
}
//修改已有属性
public static void test04() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("bean.User");
//属性
CtField cf = new CtField(CtClass.intType,"age",cc);
cf.setModifiers(Modifier.PRIVATE);
cc.addField(cf);
//增加响应的get set方法
cc.addMethod(CtNewMethod.getter("getAge",cf));
cc.addMethod(CtNewMethod.setter("setAge",cf));
//访问属性
Class clazz = cc.toClass();
Object obj = clazz.newInstance();
Field field = clazz.getDeclaredField("age");
System.out.println(field);
Method m = clazz.getDeclaredMethod("setAge", int.class);
m.invoke(obj, 16);
Method m2 = clazz.getDeclaredMethod("getAge", null);
Object resutl = m2.invoke(obj,null);
System.out.println(resutl);
}
//操作构造方法
public static void test05() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("bean.User");
CtConstructor[] cons = cc.getConstructors();
for(CtConstructor con:cons){
System.out.println(con);
}
}
public static void main(String[] args) throws Exception {
//test01();
//test02();
//test03();
//test04();
test05();
}
}