这里举个简单的例子,来记录下如何用CtClass创建一个类,并且往这个类里面新加方法,
下面是代码,可以直接运行:
package seeeyou.app.test;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;
/**
*
* @author seeeyou
*
*/
public class TestHelloWorld2 {
public static void main(String[] args) throws NotFoundException,
IOException, CannotCompileException, InstantiationException,
IllegalAccessException, SecurityException, NoSuchMethodException,
IllegalArgumentException, InvocationTargetException {
// 用于取得字节码类,必须在当前的classpath中,使用全称
ClassPool pool = ClassPool.getDefault();
/**
* makeClass() cannot create a new interface; makeInterface() in
* ClassPool can do. Member methods in an interface can be created with
* abstractMethod() in CtNewMethod. Note that an interface method is an
* abstract method.
*/
CtClass ccClass = pool.makeClass("Point");
String bodyString = "{System.out.println(\"Call to method \");}";
//为新创建的类新加一个方法execute,无任何参数
CtMethod n1 = CtNewMethod.make(CtClass.voidType, "execute", null, null,
bodyString, ccClass);
ccClass.addMethod(n1);
/**
* 这里无法用new的形式来创建一个对象,因为已经classloader中不能有两个相同的对象,否则会报异常如下:
*Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader):
*attempted duplicate class definition for name: "Point"
**/
Object oo = ccClass.toClass().newInstance();
Method mms = oo.getClass().getMethod("execute", null);
System.out.println("new class name is : " + oo.getClass().getName());
System.out.println("new class's method is : " + mms.invoke(oo, null));
System.out.println("---------------------------------------------");
//这一行代码将class冻结了,下面无法再对类多编辑或者修改,下面的setName会报异常如:
//Exception in thread "main" java.lang.RuntimeException: Point class is frozen
ccClass.freeze();
try{
ccClass.setName("Point2");
}catch (Exception e) {
System.out.println(e);
}
//对已经冻结的class解冻之后还可以继续编辑修改
ccClass.defrost();
System.out.println("------------- 上面的代码是对的,下面的代码将会无法执行出结果,会报错------------------------");
//第二个方法
bodyString = "public int getNumber(Integer num){System.out.println(\"Point2 Call to method \");return 10+num;}";
CtMethod n2 = CtNewMethod.make(bodyString, ccClass);//直接创建一个方法,带有一个int的参数和返回值
ccClass.addMethod(n2);
Class[] params = new Class[1];
Integer num = new Integer(15);
params[0] = num.getClass();
mms = oo.getClass().getMethod("getNumber", params);
System.out.println("new class name is : " + oo.getClass().getName());
System.out.println("new class's method is : " + mms.invoke(oo, 100));
System.out.println("---------------------------------------------");
}
}
上面的结果是:new class name is : Point Call to method new class's method is : null --------------------------------------------- java.lang.RuntimeException: Point class is frozen --------------------------------------------- Exception in thread "main" java.lang.NoSuchMethodException: Point.getNumber(java.lang.Integer) at java.lang.Class.getMethod(Class.java:1605) at seeeyou.app.test.TestHelloWorld2.main(TestHelloWorld2.java:66)
错误的原因其实和简单,因为我第二次新加一个方法后,没有再次实例化一个对象,所以oo还是原来的对象,他的成员函数肯定没有新加的方法。
那我可以再次实例化下试试,代码和结果如下:
package seeeyou.app.test; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.NotFoundException; /** * * @author seeeyou * */ public class TestHelloWorld2 { public static void main(String[] args) throws NotFoundException, IOException, CannotCompileException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException { // 用于取得字节码类,必须在当前的classpath中,使用全称 ClassPool pool = ClassPool.getDefault(); /** * makeClass() cannot create a new interface; makeInterface() in * ClassPool can do. Member methods in an interface can be created with * abstractMethod() in CtNewMethod. Note that an interface method is an * abstract method. */ CtClass ccClass = pool.makeClass("Point"); String bodyString = "{System.out.println(\"Call to method \");}"; //为新创建的类新加一个方法execute,无任何参数 CtMethod n1 = CtNewMethod.make(CtClass.voidType, "execute", null, null, bodyString, ccClass); ccClass.addMethod(n1); /** * 这里无法用new的形式来创建一个对象,因为已经classloader中不能有两个相同的对象,否则会报异常如下: *Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): *attempted duplicate class definition for name: "Point" **/ Object oo = ccClass.toClass().newInstance(); Method mms = oo.getClass().getMethod("execute", null); System.out.println("new class name is : " + oo.getClass().getName()); System.out.println("new class's method is : " + mms.invoke(oo, null)); System.out.println("---------------------------------------------"); //这一行代码将class冻结了,下面无法再对类多编辑或者修改,下面的setName会报异常如: //Exception in thread "main" java.lang.RuntimeException: Point class is frozen ccClass.freeze(); try{ ccClass.setName("Point2"); }catch (Exception e) { System.out.println(e); } //对已经冻结的class解冻之后还可以继续编辑修改 ccClass.defrost(); System.out.println("------------- 上面的代码是对的,下面的代码将会无法执行出结果,会报错------------------------"); //第二个方法 bodyString = "public int getNumber(Integer num){System.out.println(\"Point2 Call to method \");return 10+num;}"; CtMethod n2 = CtNewMethod.make(bodyString, ccClass);//直接创建一个方法,带有一个int的参数和返回值 ccClass.addMethod(n2); Class[] params = new Class[1]; Integer num = new Integer(15); params[0] = num.getClass();//就多了下面这个实例化,但是这样会导致一个错误 oo = ccClass.toClass().newInstance(); mms = oo.getClass().getMethod("getNumber", params); System.out.println("new class name is : " + oo.getClass().getName()); System.out.println("new class's method is : " + mms.invoke(oo, 100)); System.out.println("---------------------------------------------"); } }
这也会导致一个错误:new class name is : Point Call to method new class's method is : null --------------------------------------------- java.lang.RuntimeException: Point class is frozen ------------- 上面的代码是对的,下面的代码将会无法执行出结果,会报错------------------------ Exception in thread "main" javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "Point" at javassist.ClassPool.toClass(ClassPool.java:1051) at javassist.ClassPool.toClass(ClassPool.java:994) at javassist.ClassPool.toClass(ClassPool.java:952) at javassist.CtClass.toClass(CtClass.java:1079) at seeeyou.app.test.TestHelloWorld2.main(TestHelloWorld2.java:66) Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "Point" at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:621) at java.lang.ClassLoader.defineClass(ClassLoader.java:466) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at javassist.ClassPool.toClass2(ClassPool.java:1063) at javassist.ClassPool.toClass(ClassPool.java:1045) ... 4 more
原因也很简单,一个classloader里面怎么有两个重复的对象呢,除非是两个不同的classloader。。所以爆了个重复加载类的错误
对的方式是只实例化一次:如下:
package seeeyou.app.test; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.NotFoundException; /** * * @author seeeyou * */ public class TestHelloWorld3 { public static void main(String[] args) throws NotFoundException, IOException, CannotCompileException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException { // 用于取得字节码类,必须在当前的classpath中,使用全称 ClassPool pool = ClassPool.getDefault(); /** * makeClass() cannot create a new interface; makeInterface() in * ClassPool can do. Member methods in an interface can be created with * abstractMethod() in CtNewMethod. Note that an interface method is an * abstract method. */ CtClass ccClass = pool.makeClass("Point"); String bodyString = "{System.out.println(\"Call to method \");}"; //为新创建的类新加一个方法execute,无任何参数 CtMethod n1 = CtNewMethod.make(CtClass.voidType, "execute", null, null, bodyString, ccClass); ccClass.addMethod(n1); //新加第二个方法 bodyString = "public Integer getNumber(Integer num);"; CtMethod n2 = CtNewMethod.make(bodyString, ccClass);//直接创建一个方法,带有一个int的参数和返回值 n2.setBody("{System.out.println(\"Point Call to method \");return $1;}"); ccClass.addMethod(n2); /** * 这里无法用new的形式来创建一个对象,因为已经classloader中不能有两个相同的对象,否则会报异常如下: *Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): *attempted duplicate class definition for name: "Point" **/ Object oo = ccClass.toClass().newInstance(); Method mms = oo.getClass().getMethod("execute", null); System.out.println("new class name is : " + oo.getClass().getName()); System.out.println("new class's method is : " + mms.invoke(oo, null)); System.out.println("---------------------------------------------"); //这一行代码将class冻结了,下面无法再对类多编辑或者修改,下面的setName会报异常如: //Exception in thread "main" java.lang.RuntimeException: Point class is frozen ccClass.freeze(); try{ ccClass.setName("Point2"); }catch (Exception e) { System.out.println(e); } //对已经冻结的class解冻之后还可以继续编辑修改 ccClass.defrost(); System.out.println("------------- 上面的代码是对的,下面的代码将会无法执行出结果,会报错------------------------"); Class[] params = new Class[1]; Integer num = new Integer(0); params[0] = num.getClass(); mms = oo.getClass().getMethod("getNumber",params); System.out.println("new class name is : " + oo.getClass().getName()); System.out.println("new class's method is : " + mms.invoke(oo, 100)); System.out.println("---------------------------------------------"); } }
结果如下:new class name is : Point Call to method new class's method is : null --------------------------------------------- java.lang.RuntimeException: Point class is frozen ------------- 上面的代码是对的,下面的代码将会无法执行出结果,会报错------------------------ new class name is : Point Point2 Call to method new class's method is : 100 ---------------------------------------------