1.字节码操作
JAVA动态性的两种常见实现方式
字节码操作
反射
运行时操作字节码可以让我们实现如下功能
动态生成新的类
动态改变某个类的结构(添加/删除/修改 新的属性/方法)
优势
比反射开销小,性能高
JAVAasist性能高于反射,低于asm
2.常见的字节码操作类库
BCEL
Byte Code Engineering Library (BCEL), 这是Apache Software Foundation 的 Jakarta 项目的一部分.BCEL是Java classworking广泛使用的一种框,它可以让您深入JVM汇编语言进行类操作的细节.BCEL与Javassist有不同的处理字节码方法,BCEL在实际的JVM指令层次上进行操作(BCEI拥有丰富的JVM指令级支持)而Javassist所强调的是源代码级别的工作
ASM
是一个轻量级ava字节码操作框架,直接涉及量到VM底层的操作和指令
CGLIB(Code Generation Library)
是一个强大的,高性能,高质量的Code生成类库,基于ASM实现
Javassist
是一个开源的分析、编辑和创建Jaw字节码的类库.性能较ASM差,跟cglib差不多,但是使用简单.很多开源框架都在使用它
3.JAVAssist库
Javassist(Java Programming Assistant)makes java bytecode manipulation simple.
It is a class library for editing bytecodes in Java;it enables Java programs to define a new class at runtime and to modify a class file when the JVM loads it.
Unlike other similar bytecode editors,Javassist provides two levels of API:source level and bytecode level.
If the users use the source-level API,they can edit a class file without knowledge of the specifications of the Java bytecode.The whole API is designed with only the vocabulary of the java language.You can even specify inserted bytecode-level API allows the users to directly edit a class file as other editors.
Aspect Oriented Programming(AOP面向切面编程):Javassist can be a good tool for adding new methods into a class and for inserting before/after/around advice at the both caller and callee sides.
Reflection:Ones of applications of Javassist is runtime reflection;Javassist enables Java programs to use a metaobject that controls method calls on base-level objects.No specialized complier or virtual machine are needed.
4.JAVAssist库的API简介
javaassist的最外层的API和JAVA的反射包中的API颇为类似
它主要由CtClass,CtMethod,以及CtField几个类组成.用以执行和JDK反射API中java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method.Field相同的操作(Ct为Complie Time)
5.JAVAssist库的简单使用
创建一个全新的类
使用XJAD反编译工具,将生成的class文件反编译成JAVA文件
使用前先导入javassist的jar包
Demo:
/**
* 使用javassist生成一个新的类
* @author Matrix42
*
*/
public class Demo01 {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("com.lorinda.bean.Emp");
//创建属性
CtField f1 = CtField.make("private int empno;", cc);
CtField f2 = CtField.make("private String ename;", cc);
cc.addField(f1);
cc.addField(f2);
//创建方法
CtMethod m1 = CtMethod.make("public int getEmpno(){return empno;}", cc);
CtMethod m2 = CtMethod.make("public void setEmpno(int empno){this.empno = empno;}", cc);
cc.addMethod(m1);
cc.addMethod(m2);
CtMethod m3 = CtMethod.make("public String getEname(){return ename;}", cc);
CtMethod m4 = CtMethod.make("public void setEname(String empno){this.ename = ename;}", cc);
cc.addMethod(m3);
cc.addMethod(m4);
//添加构造器
CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")}, cc);
constructor.setBody("{this.empno=$1;this.ename=$2;}");
cc.addConstructor(constructor);
//将上面构造好的类写入到d:/myjava
cc.writeFile("d:/myjava");
System.out.println("生成类,成功!");
}
}
创建完成后使用XJAD反编译就可以看到源码了
反编译源码:
package com.lorinda.bean;
public class Emp
{
private int empno;
private String ename;
public int getEmpno()
{
return empno;
}
public void setEmpno(int i)
{
empno = i;
}
public String getEname()
{
return ename;
}
public void setEname(String s)
{
ename = ename;
}
public Emp(int i, String s)
{
empno = i;
ename = s;
}
}
6.JAVAssist库的API详解
方法操作
修改已有方法的方法体(插入代码到已有方法体)
新增方法
删除方法
a
b
c
\$0,\$1,\$2,...
this and actual parameters
\$0 代表的是 this,\$1 代表方法参数的第一个参数,\$2 代表方法参数的第二个参数, 以此类推,$N 代表方法参数的第 N 个参数
\$args
An arrar of parameters
The type of $args is Object[], \$args[0] 对应的是 \$1 而不是 \$0
$$
所有方法参数的简写, 主要用在方法调用上
move(String a,String b) move(\$\$) 相当于 move(\$1,\$2)
fallthrough
path
在类路径, 源文件路径等中有不存在的路径警告
\$cflow
\$r
\$_
addCatch()
\$class
\$sig
属性操作
修改已有方法的方法体(插入代码到已有方法体)
新增方法
删除方法
Demo:
import java.awt.color.CMMException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class Demo02 {
public static void test01() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("javassist.Emp");
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()); //获得接口
}
public static void test02()throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("javassist.Emp");
//CtMethod m = CtMethod.make("public int add(int a,int b){return a+b;}", cc);
CtMethod m = new CtMethod(CtClass.intType,"add",
new CtClass[]{CtClass.intType,CtClass.intType},cc);
m.setModifiers(Modifier.PUBLIC);
m.setBody("{System.out.println(\"Ha Ha\");return $1+$2;}");
cc.addMethod(m);
//通过反射调用新生产的方法
Class> clazz = cc.toClass();
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("add", int.class,int.class);
Object result = method.invoke(obj, 200,300);
System.out.println(result);
}
public static void test03()throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("javassist.Emp");
CtMethod cm = cc.getDeclaredMethod("sayHello", new CtClass[]{CtClass.intType});
cm.insertBefore("System.out.println($1);");
Class> clazz = cc.toClass();
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("sayHello", int.class);
method.invoke(obj, 90);
}
public static void test04() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("javassist.Emp");
//CtField f1 = CtField.make("private int empno", cc);
CtField f1 = new CtField(CtClass.intType,"salary",cc);
f1.setModifiers(Modifier.PRIVATE);
cc.addField(f1,"1000");//1000位默认值
// cc.getDeclaredField("ename"); //获取指定属性
cc.addMethod(CtNewMethod.getter("salary",f1));
cc.addMethod(CtNewMethod.setter("salary", f1));
}
public static void test05()throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("javassist.Emp");
CtConstructor[] cs = cc.getConstructors();
for (CtConstructor ctConstructor : cs) {
System.out.println(ctConstructor.getLongName());
}
}
public static void main(String[] args) throws Exception {
//test01();
//test02();
// test03();
// test04();
test05();
}
}
构造方法操作
getConstructors()
注解操作
代码片段:
public @interface Author {
String name();
int year();
}
@Author(name="Chiba",year=2005)
public class Point{
int x,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:"+name+",year:"+year);
当调用了writeFile(),toClass(),toBytecode(),Javassist会把那个CtClass对象冻结,如果想使用冻结的对象可以调用.defrose()方法
局限性
JDK5.0新语法不支持(包括泛型,枚举),不支持注解修改,单可以的通过底层javasist类来解决,具体参考:javassist.bytecode.annotation
不支持数组的初始化,如String[]{"1","2"},除非只有数组容量为1
不支持内部类盒匿名类
不支持continue盒break表达式
对于继承关系,有些语法不支持,如:
class A{}
class B extends A{}
class C extends B{}