javassist学习3

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 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
---------------------------------------------