java本身动态性的两种实现方式:反射和字节码操作
字节码操作:可以实现动态生成一个类(.class文件)
和动态的修改类的结构;可以去改变一个类。
反射:去动态的执行类的内容,不能改变类。一个是改变,一个是执行;多数情况下两者配合使用。
反射中有Class、 Method、Field、Constructor等类,相似在字节码操作中有CtClass、CtMethod、CtField、CtConstructor等与之相对应。 Ct(Compile time)。
MakeNewClass:利用动态字节码操作生成一个新的类;
ChangeClass :去改变一个类的结构
User: 需要改变的类
package com.test.byteCode;
import java.io.IOException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
/**
* java的动态性中对字节码的操作,
* 利用javasisit生成一个新的.class文件
* @author zhb
*
*/
public class MakeNewClass {
public static void main(String[] args) throws CannotCompileException, IOException {
ClassPool pool = ClassPool.getDefault();
String className = "com.test.reflect.User";
// 用类的全路径创建一个类
CtClass ctClass = pool.makeClass(className);
// 给类创建属性
CtField ctField = CtField.make("private int id;", ctClass);
// 给类添加属性
ctClass.addField(ctField);
// 给类创建构造方法
// 创建有参的构造方法
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType}, ctClass);
ctConstructor.setBody("this.id = id;");
// 给类添加构造方法
ctClass.addConstructor(ctConstructor);
// 给类创建方法
CtMethod ctMethod1 = CtMethod.make("private void setId(String id){this.id = id;}", ctClass);
CtMethod ctMethod2 = CtMethod.make("private String getId(){return id;}", ctClass);
CtMethod ctMethod3 = CtMethod.make("public void sayHello(){System.out.println(\"你好\");}", ctClass);
// 给类添加方法
ctClass.addMethod(ctMethod1);
ctClass.addMethod(ctMethod2);
ctClass.addMethod(ctMethod3);
// 生成class文件,到D盘Myclass的文件夹下
ctClass.writeFile("E:/Myclass");
System.out.println("class文件创建成功");
}
}
package com.test.byteCode;
import java.lang.reflect.Method;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
/**
* 更改User的class文件的内容
* @author zhb
*
*/
public class ChangeClass {
/**
* 用字节码动态添加一个方法,并且执行这个方法
* @throws Exception
*/
public static void test1() throws Exception{
ClassPool classPool = ClassPool.getDefault();
String path = "com.test.reflect.User";
CtClass ctClass = classPool.get(path);
// 给类添加一个新方法
CtMethod ctMethod = CtMethod.make("public int add(int a, int b){return a+b;}", ctClass);
ctClass.addMethod(ctMethod);
// 通过反射执行刚刚加入的add方法
// ctClass转成反射类
Class clazz = ctClass.toClass();
Object obj = clazz.newInstance();
Method method = clazz.getMethod("add", int.class, int.class);
// 执行刚刚加入的add方法
Object result = method.invoke(obj, 8, 4);
ctClass.defrost();
System.out.println(result);
}
/**
* 用字节码动态添加动态给一个方法的前面或者后面添加方法,并且执行这个方法
* @throws Exception
*/
public static void test2() throws Exception{
ClassPool classPool = ClassPool.getDefault();
String path = "com.test.reflect.User";
CtClass ctClass = classPool.get(path);
// 修改方法,在某个方法的前面或者后面加入新的方法;像spring AOP
// 获取原有的方法,hello
CtMethod ctMethod = ctClass.getDeclaredMethod("hello", new CtClass[]{classPool.get("java.lang.String")});
ctMethod.insertBefore("System.out.println(\"方法之前执行\");");
ctMethod.insertAfter("System.out.println(\"方法之后执行\");");
Class clazz = ctClass.toClass();
Object obj = clazz.newInstance();
Method method = clazz.getMethod("hello", String.class);
method.invoke(obj,"张三");
}
public static void main(String[] args) throws Exception {
test1();
// test2();
}
}
package com.test.reflect;
public class User {
private int id;
private String name;
// 无参的构造方法,容易被忘记
public User(){
}
//有参的构造方法
public User(int id, String name){
this.id = id;
this.name = name;
}
// 类中一个普通的方法
public String hello(String name){
System.out.println("hello:"+ name);
return "方法调用成功";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}