javassist学习三
这里举个简单的例子,来记录下如何用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 frozenccClass.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 : PointCall 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 frozenccClass.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 : PointCall 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 frozenccClass.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 : PointCall to method new class's method is : null---------------------------------------------java.lang.RuntimeException: Point class is frozen------------- 上面的代码是对的,下面的代码将会无法执行出结果,会报错------------------------new class name is : PointPoint2 Call to method new class's method is : 100---------------------------------------------
?