javassist.jar是个非常不错的classCode修改框架,简单实用
通过javassist和classLoader结合,在加载class时修改class,类似于Spring AOP,可以在指定的class中额外加载指定的功能
下面的例子中,在AppClassLoader的classPath中包含的class都加入了方法信息打印(参数打印、方法调用栈),方法返回信息打印。
利用这个,如果要看Spring,Hibernate的大点的框架时,通过这个运行一个例子,可以很方便了解方法的调用时序图(调用堆栈),每个参数的值。
当然这个例子很简陋,比如打印参数只能打印基本类型,如果是高级对象需要重写,解析。
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
/**
* 该类的主要原理是通过类加载时修改classCode,修改的方法可以是对方法的修改,新增,删除,属性的修改,新增删除,等其他操作
* 通过这个例子,可以很方便的对程序进行日志跟踪,排错,流程理解等
*
* @author jin.xiong
*
*/
public class JavassistLoader extends ClassLoader {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) throws Throwable {
JavassistLoader s = new JavassistLoader();
Class c = s.loadClass("com.benx.Test");
c.getDeclaredMethod("main", new Class[] { String[].class }).invoke(null, new Object[] { args });
}
private ClassPool pool;
private ClassLoader classLoader;
public JavassistLoader() throws NotFoundException {
/**
* 设置classLoader的parent为ExtClassLoader,如果没有设置,将默认使用AppClassLoader,
* 因为classLoader的加载顺序是从parent
* --child的相亲委派加载关系,所以如果不设置那么classPath的class将不会使用自定义加载器
*
* 如果加载的class为外部的,那么可以不设置parent,使用默认
*/
super(Thread.currentThread().getContextClassLoader().getParent());
classLoader = Thread.currentThread().getContextClassLoader();
String[] classPath = System.getProperty("java.class.path").split(";");
pool = ClassPool.getDefault();
for (String path : classPath) {
System.out.println(path);
pool.appendClassPath(path);
}
// TODO 可以自定义需要加载的类
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Class findClass(String name) throws ClassNotFoundException {
// TODO 可以对class过滤
try {
CtClass cc = pool.get(name);
System.out.println(cc.isFrozen() + " " + cc.isInterface() + " " + cc.isModified() + " " + cc.isPrimitive());
if (!cc.isInterface()) {
CtMethod[] ms = cc.getDeclaredMethods();
for (CtMethod m : ms) {
if (m.isEmpty())
continue;
// TODO 可以对method过滤
StringBuffer sb = new StringBuffer("");
sb.append("{");
// TODO 可以指定修改的内容
m.insertBefore(getStackTraceCode().toString() + getParaCode(m).toString());
m.insertAfter(getReturnCode().toString());
sb.append("}");
}
}
byte[] b = cc.toBytecode();
return defineClass(name, b, 0, b.length);
} catch (Exception e) {
// e.printStackTrace();
Class cls = classLoader.loadClass(name);
return cls;
// throw new ClassNotFoundException();
}
}
private StringBuffer getReturnCode() {
StringBuffer temp = new StringBuffer("");
temp.append("StackTraceElement[] as = Thread.currentThread().getStackTrace();");
temp.append("System.out.println(\"class:\"+as[1].getClassName()+\" method:\"+as[1].getMethodName() +\" return:\"+$_);");
return temp;
}
/**
* 方法参数内容显示的嵌入代码
*
* @param cm
* @return
* @throws NotFoundException
*/
private StringBuffer getParaCode(CtMethod cm) throws NotFoundException {
StringBuffer temp = new StringBuffer("");
temp.append(" Object[] ob = $args; ");
temp.append(" StringBuffer sb = new StringBuffer();");
temp.append(" sb.append(\"[\");");
temp.append(" for (int i = 0; i < ob.length; i++) {");
temp.append(" sb.append(ob[i]+\",\");");
temp.append(" }");
temp.append(" sb.append(\"]\");");
temp.append(" System.out.println(\"parameter is:\"+sb);");
return temp;
}
/**
* 方法调用栈的嵌入代码
*
* @return
*/
private StringBuffer getStackTraceCode() {
StringBuffer sb = new StringBuffer("");
sb.append(" StackTraceElement[] as = Thread.currentThread().getStackTrace();");
sb.append(" System.out.print(\"class:\"+as[1].getClassName()+\" method:\"+as[1].getMethodName());");
sb.append(" StringBuffer temp = new StringBuffer();");
sb.append(" for (int i = (as.length - 1); i > 0; i--) {");
sb.append(" temp.append(as[i].getClassName() + \".\" + as[i].getMethodName() + \" \");");
sb.append(" }");
sb.append(" System.out.print(\" StackTrace:\");");
sb.append(" System.out.print(temp.toString());");
return sb;
}
}