Java学习之动态编译--字节码操作--javassist类库

一、字节码操作

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个
$argsThe type of $args is OBject[]. $args(0)对应的是$1,不是$0
$$一个方法调用的深度
$r方法返回值的类型
$_方法返回值。(修改方法体时不支持)
addCatch()方法中加入try catch块  $e代表 异常对象
$classthis的类型(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);


四、局限性:

  1. JDK5.0新语法不支持(包括泛型,枚举),不支持注解修改,但可以通过底层的javassist类来解决,具体参考:javassist,bytecode.annotation
  2. 不支持数组的初始化,如String[]{"a","b"},除非只有数组的容量为1
  3. 不支持内部类和匿名类
  4. 不支持continue和break 表达式
  5. 对于继承关系,有些不支持 。例如
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();
	}

}

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值