首先加先添加依赖项目
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>
1.使用情况 创建一个类对象 并使用
大概的使用其实就是用各种的api 程序的意思如下
创建一个新类
1.创建成员变量(类型,所属的类) 2. 设置访问修饰符 3. 放进类里
2.创建方法 有两种方式
第一种是
1.先创建方法(返回值类型,名称,参数列表)
2.设置访问修饰符
3.添加方法体 其实就是之间像是源码一样 写 传入的参数按参数列表的来 $1,$2,$3,...代替
4.放进类里面
第二种是
1.创建方法 直接 “ 就java的源码 ”
2.放入类里面
3.创建构造函数
如果没有 默认有一个无参构造函数 如果创建了个有参的 放进去了那么 就 不会默认提供无参的
创建也有两种方式 和创建方法类似
第一种
1.先创建方法(参数列表,所属类)
2.设置访问修饰符
3.添加方法体 其实就是之间像是源码一样 写 传入的参数按参数列表的来 $1,$2,$3,...代替
4.放进类里面
第二种
1.创建方法 直接 “ 就java的源码 ”
2.放入类里面
至于新创建完这个类 运行后不会出现真正的源码文件以及class
创建完这个类后通过.toClass()变成java的类对象 因为不可以通过正常的调用 只能通过反射来调用
public class JavassistTests2 {
public static void main(String[] args) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 类库, jvm 中所加载的 class
ClassPool pool = ClassPool.getDefault();
// 创建一个新的类, 类名必须为全量类名
CtClass tClass = pool.makeClass("tests.bytecode.javassist.TC2");
// 构建新的类的成员变量
CtField ctFieldNew = new CtField(CtClass.intType,"age",tClass);
// 设置新的类的成员变量访问修饰符为 public
ctFieldNew.setModifiers(Modifier.PUBLIC);
// 将属性添加到类中
tClass.addField(ctFieldNew);
//创建新的方法, 参数 1:方法的返回类型,参数 2:名称,参数 3:方法的参数,参数 4:方法所属的类
CtMethod newMethod1 = new CtMethod(CtClass.intType, "newfun1", new CtClass[]
{CtClass.intType,CtClass.intType},tClass);
// 设置方法的访问修饰
newMethod1.setModifiers(Modifier.PUBLIC);
// 将新建的方法添加到类中
tClass.addMethod(newMethod1);
// 方法体内容代码 $1 代表第一个参数,$2 代表第二个参数
newMethod1.setBody("return $1 + $2;");
// 也可以直接创建方法
CtMethod newMethod2 = CtNewMethod.make("public String newfun2(String str) { return \"newfun2型参\"+str;}", tClass);
tClass.addMethod(newMethod2);
// 创建新的构造方法
CtConstructor tConstructor1= new CtConstructor(new CtClass[]{},tClass);
tConstructor1.setModifiers(Modifier.PUBLIC);
tConstructor1.setBody(null);
tClass.addConstructor(tConstructor1);
// 也可以直接创建构造函数
CtConstructor tConstructor2=CtNewConstructor.make("public TC2(int age){this.age=age;}",tClass);
tClass.addConstructor(tConstructor2);
Class tc2=tClass.toClass();
System.out.println(tc2);
//有了类对象就可以利用反射来使用这个类了
//获取类的所有方法 会得到newfun1 newfun2
Method[] methods=tc2.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
System.out.println();
System.out.println();
//获取这个类的属性 会得到age
Field[] fields = tc2.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
//查看是否有无参构造方法 用反射去调用它无参构造方法创造对象
//结果tests.bytecode.javassist.TC2@7a46a697
System.out.println(tc2.newInstance());
//通过反射调用方法
Method newfun1=tc2.getDeclaredMethod("newfun1",int.class,int.class);
System.out.println(newfun1.invoke(tc2.newInstance(),1 ,2));
//通过反射调用方法
Method newfun2=tc2.getDeclaredMethod("newfun2",String.class);
System.out.println( newfun2.invoke(tc2.newInstance(),"你是狗吗" ));
}
}
下面开始是对已有类的操作
public class TC1 {
private int pr;
public int pu;
public TC1(int one) {
}
public TC1() {
}
public void puvfun(){
System.out.println(" public void puvfun");
}
private void prvfun(){
System.out.println("private void prvfun");
}
public static void pusvfun(){
System.out.println("public static void pusvfun");
}
public static void prsvfun(){
System.out.println("public static void prsvfun");
}
public String pusfun(){
System.out.println(" public void pusfun");
return " public void pusfun";
}
private String prsfun(){
System.out.println("private void prsfun");
return "private void prsfun";
}
public static String pussfun(){
System.out.println("public static void pussfun");
return "public static void pussfun";
}
public static String prssfun(){
System.out.println("public static void prssfun");
return "public static void prssfun";
}
}
2.使用情况 给已有的一个类添加属性 方法 并使用
创建成员变量 和 创建方法是跟上文一样的
注意这次是 CtClass tClass = pool.get("tests.bytecode.javassist.TC1"); 是获得原有的这个类 不是创建新的类 如果有那个方法名了 不是重载会报错
public static void main(String[] args) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 类库, jvm 中所加载的 class
ClassPool pool = ClassPool.getDefault();
// 创建一个新的类, 类名必须为全量类名
CtClass tClass = pool.get("tests.bytecode.javassist.TC1");
// 构建新的类的成员变量
CtField ctFieldNew = new CtField(CtClass.intType,"age",tClass);
// 设置新的类的成员变量访问修饰符为 public
ctFieldNew.setModifiers(Modifier.PUBLIC);
// 将属性添加到类中
tClass.addField(ctFieldNew);
//创建新的方法, 参数 1:方法的返回类型,参数 2:名称,参数 3:方法的参数,参数 4:方法所属的类
CtMethod newMethod1 = new CtMethod(CtClass.intType, "newfun1", new CtClass[]
{CtClass.intType,CtClass.intType},tClass);
// 设置方法的访问修饰
newMethod1.setModifiers(Modifier.PUBLIC);
// 将新建的方法添加到类中
tClass.addMethod(newMethod1);
// 方法体内容代码 $1 代表第一个参数,$2 代表第二个参数
newMethod1.setBody("return $1 + $2;");
//也可以直接创建方法
CtMethod newMethod2 = CtNewMethod.make("public String newfun2(String str) { return \"newfun2型参\"+str;}", tClass);
tClass.addMethod(newMethod2);
// 也可以直接创建构造函数
CtConstructor tConstructor2=CtNewConstructor.make("public TC1(int age,int a){this.age=age;}",tClass);
tClass.addConstructor(tConstructor2);
Class tc11=tClass.toClass();
Class tc12=TC1.class;
System.out.println(tc11==tc12);
//有了类对象就可以利用反射来使用这个类了
//获取类的所有方法 会得到newfun1 newfun2
Method[] methods=tc12.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
System.out.println();
System.out.println();
//获取这个类的属性 会得到age
Field[] fields = tc12.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
//查看是否有无参构造方法 用反射去调用它无参构造方法创造对象
//结果tests.bytecode.javassist.TC2@7a46a697
System.out.println(tc12.newInstance());
//通过反射调用方法
Method newfun1=tc12.getDeclaredMethod("newfun1",int.class,int.class);
System.out.println(newfun1.invoke(tc12.newInstance(),1 ,2));
//通过反射调用方法
Method newfun2=tc12.getDeclaredMethod("newfun2",String.class);
System.out.println( newfun2.invoke(tc12.newInstance(),"你是狗吗" ));
}
结果如下
true
newfun1
newfun2
prvfun
prsfun
puvfun
prsvfun
pusfun
pusvfun
pussfun
prssfun
pr
pu
age
tests.bytecode.javassist.TC1@7a46a697
3
newfun2型参你是狗吗
注意 这时候 修改完的类 和 原有的类 的对象是同一个了 就是
3.使用情况 给已有的一个类的方法进行增强
public static void main(String[] args) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException {
//使用这个javassist 修改的这个类不能先加载到JVM里
//TC1 tc =new TC1();
//拿到类对象
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("tests.bytecode.javassist.TC1");
//拿到这个方法对象
CtMethod puvfunM=cc.getDeclaredMethod("puvfun");
//对这个方法进行增强
puvfunM.insertBefore("{System.out.println(\"在前面加强了方法\");}");
puvfunM.insertAfter("{System.out.println(\"在后面加强了方法\");}");
//变成Java的类对象
Class c=cc.toClass();
//实例化 通过这个类对象反射创建
TC1 tc1 =(TC1) c.newInstance();
tc1.puvfun();
//用普通的创建实例
TC1 tc2 =new TC1();
tc2.puvfun();
}
结果如下
在前面加强了方法
public void puvfun
在后面加强了方法
在前面加强了方法
public void puvfun
在后面加强了方法
注意:修改这个类之前这个类不能加载到JVM里 也就是它还没有加载前才可以 否则结果如下
Exception in thread "main" javassist.CannotCompileException: by java.lang.ClassFormatError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "tests/bytecode/javassist/TC1"
at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:271)
at javassist.ClassPool.toClass(ClassPool.java:1240)
at javassist.ClassPool.toClass(ClassPool.java:1098)
at javassist.ClassPool.toClass(ClassPool.java:1056)
at javassist.CtClass.toClass(CtClass.java:1298)
at tests.bytecode.javassist.JavassistTests.main(JavassistTests.java:18)
Caused by: java.lang.ClassFormatError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "tests/bytecode/javassist/TC1"
at javassist.util.proxy.DefineClassHelper$Java7.defineClass(DefineClassHelper.java:182)
at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:260)
... 5 more
总结:
目前的学习情况来看 并不能做热更新=。=