会使用Javassist的基本操作
Java动态性的两种常见实现方式:
运行时操作字节码有什么用:
与反射相比的优势:
- BCEL
Byte Code Engineering Library (BCEL),这是Apache Software Foundation 的Jakarta项目的一
部分。BCEL是Java classworking广泛使用的一种框架,它可以让您深入JVM汇编语言进行类操作的细节。BCEL 与Javassist有不同的处理字节码方法,BCEL在实际的JVM指令层次上进行操作(BCEL拥有丰富的JVM指令级支持)而Javassist所强调的是源代码级别的工作。
- ASM
是一个轻量级java字节码操作框架 ,直接涉及 到VM底层的操作和指令
- CGLIB (Code Generation Library)
是一个强大的,高性能,高质量的Code生成类库,基于ASM实现。
- Javassist
是一个开源的分析、 编辑和创建Java字节码的类库。性能较ASM差,跟cglib差不多,但是使用简单。
很多开源框架都在使用它。
●javassist的最外层的API和JAVA的反射包中的API颇为类似。
● 它主要由CtClass, , CtMethod, ,以及CtField几个类组成。用以执行和JDK反射API中java.lang.Class, , java.lang.reflect.Method,,java.lang.reflect.Method .Field相同的操作。
要使用Javassist库需要上网下载响应的jar包,可以在 https://mvnrepository.com 这个网站上下载,挺好用的。
package com.xyj.JAVAssist;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
/**
* 测试使用javassist生成一个新的类
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
//获得类池
ClassPool pool=ClassPool.getDefault();
//获取CtClass
CtClass cc = pool.makeClass("com.xyj.entity.Emp");
//创建属性
CtField field1 = CtField.make("private int empNo;",cc);
CtField field2 = CtField.make("private String empName;",cc);
cc.addField(field1);
cc.addField(field2);
//创建方法
CtMethod method1 = CtMethod.make("public int getEmpNo(){return empNo;}",cc);
CtMethod method2 = CtMethod.make("public void setEmpNo(int empNo){empNo=empNo;}",cc);//$0代表this关键字 $1代表第一个参数 $2代表第二个参数
cc.addMethod(method1);
cc.addMethod(method2);
//添加构造器
CtConstructor constructor1=new CtConstructor(new CtClass[] {CtClass.intType,pool.get("java.lang.String")}, cc);
constructor1.setBody("{empNo=$1;empName=$2;}");
CtConstructor constructor2=new CtConstructor(new CtClass[] {}, cc);
constructor2.setBody("{}");
cc.addConstructor(constructor1);
cc.addConstructor(constructor2);
cc.writeFile("f:/MyJava");//将上面构建好的类写入到f:/MyJava中
System.out.println("生成类成功");
}
}
经过XJad反编译工具反编译我们刚刚动态编译好的class文件,如下:
package com.xyj.JAVAssist;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
/**
* 测试javassist的API
* javassist发明人 Chiba
*/
public class Demo02 {
/**
* 处理类的基本用法
* @throws Exception
*/
public static void test01() throws Exception {
ClassPool pool=ClassPool.getDefault();
CtClass cc = pool.get("com.xyj.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());//获得实现的接口
}
/**
* 处理方法的基本用法 测试产生新的方法
* @throws Exception
*/
public static void test02() throws Exception {
ClassPool pool=ClassPool.getDefault();
CtClass cc = pool.get("com.xyj.JAVAssist.Emp");//获取已有的类的信息
//CtMethod method = CtNewMethod.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.setBody("{System.out.println(\"add Method in here.\");return $1+$2;}");//$0代表this关键字 $1代表第一个参数 $2代表第二个参数
m.setModifiers(Modifier.PUBLIC);
cc.addMethod(m);
//通过反射调用新生成的方法
Class<?> clazz=cc.toClass();
Object obj = clazz.newInstance();//通过调用Emp的无参构造,创建新的对象
Method method=clazz.getDeclaredMethod("add",int.class,int.class);
Object result = method.invoke(obj,200,300);
System.out.println(result);
}
/**
* 处理现有方法 在方法里面的前后插入代码
* @throws Exception
*/
public static void test03() throws Exception {
ClassPool pool=ClassPool.getDefault();
CtClass cc = pool.get("com.xyj.JAVAssist.Emp");//获取已有的类的信息
CtMethod method = cc.getDeclaredMethod("sayHello",new CtClass[] {pool.get("java.lang.String")});
method.insertBefore("System.out.println(\"上午好\");");
method.insertAt(9,"System.out.println(\"好烦哦\");");//在第几行插入代码
method.insertAfter("System.out.println(\"下午好\");");
Class<?> class1 = cc.toClass();
Object object = class1.newInstance();
Method method2 = class1.getDeclaredMethod("sayHello",String.class);
method2.invoke(object,"李四");
}
/**
* 处理属性
*/
public static void test04() throws Exception {
ClassPool pool=ClassPool.getDefault();
CtClass cc = pool.get("com.xyj.JAVAssist.Emp");
//CtField f=CtField.make("private int empNo",cc);
CtField field=new CtField(CtClass.intType,"empAge",cc);
field.setModifiers(Modifier.PRIVATE);
cc.addField(field,"1000");//"1000"是默认值 可不写
//CtField declaredField = cc.getDeclaredField("empAge");//获取指定的属性
//增加响应的get set方法
CtNewMethod.getter("getEmpAge",field);
CtNewMethod.setter("setEmpAge",field);
}
/**
* 处理构造器
* @throws Exception
*/
public static void test05() throws Exception{
ClassPool pool=ClassPool.getDefault();
CtClass cc = pool.get("com.xyj.JAVAssist.Emp");
CtConstructor[] constructors = cc.getConstructors();
for (CtConstructor c : constructors) {
System.out.println(c.getLongName());
}
}
/**
* 处理注解
*/
public static void test06() throws Exception{
CtClass cc = ClassPool.getDefault().get("com.xyj.JAVAssist.Emp");
Object[] annotations = cc.getAnnotations();
Author e=(Author)annotations[0];
String name = e.name();
int year = e.year();
System.out.println("name :"+name+",year :"+year);
}
public static void main(String[] args) throws Exception {
//test01();
//test02();
//test03();
//test04();
//test05();
test06();
}
}
Author注解如下:
package com.xyj.JAVAssist;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
String name();
int year();
}
Emp类如下:
package com.xyj.JAVAssist;
@Author(name="xyj",year=2019)
public class Emp {
private int empNo;
private String empName;
public void sayHello(String user) {
System.out.println(user+"你好呀");
}
public int getEmpNo() {
return empNo;
}
public void setEmpNo(int empNo) {
this.empNo = empNo;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Emp() {
}
public Emp(int empNo, String empName) {
this.empNo = empNo;
this.empName = empName;
}
}