Javassist简单应用小结

来源:互联网 发布:增值税批量开票软件 编辑:程序博客网 时间:2024/06/01 10:00

概述

  Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果。熟练使用这套工具,可以让Java编程更接近与动态语言编程。
  下面一个方法的目的是获取一个类加载器(ClassLoader),以加载指定的.jar或.class文件,在之后的代码中会使用到。
private static ClassLoader getLocaleClassLoader() throws Exception {List<URL> classPathURLs = new ArrayList<>();// 加载.class文件路径classPathURLs.add(classesPath.toURI().toURL());// 获取所有的jar文件File[] jarFiles = libPath.listFiles(new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {return name.endsWith(".jar");}});Assert.assertFalse(ObjectHelper.isArrayNullOrEmpty(jarFiles));// 将jar文件路径写入集合for (File jarFile : jarFiles) {classPathURLs.add(jarFile.toURI().toURL());}// 实例化类加载器return new URLClassLoader(classPathURLs.toArray(new URL[classPathURLs.size()]));}

获取类型信息

@Testpublic void test() throws NotFoundException {// 获取默认类型池对象ClassPool classPool = ClassPool.getDefault();// 获取指定的类型CtClass ctClass = classPool.get("java.lang.String");System.out.println(ctClass.getName());// 获取类名System.out.println("\tpackage " + ctClass.getPackageName());// 获取包名System.out.print("\t" + Modifier.toString(ctClass.getModifiers()) + " class " + ctClass.getSimpleName());// 获取限定符和简要类名System.out.print(" extends " + ctClass.getSuperclass().getName());// 获取超类// 获取接口if (ctClass.getInterfaces() != null) {System.out.print(" implements ");boolean first = true;for (CtClass c : ctClass.getInterfaces()) {if (first) {first = false;} else {System.out.print(", ");}System.out.print(c.getName());}}System.out.println();}

修改类方法

@Testpublic void test() throws Exception {// 获取本地类加载器ClassLoader classLoader = getLocaleClassLoader();// 获取要修改的类Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");// 实例化类型池对象ClassPool classPool = ClassPool.getDefault();// 设置类搜索路径classPool.appendClassPath(new ClassClassPath(clazz));// 从类型池中读取指定类型CtClass ctClass = classPool.get(clazz.getName());// 获取String类型参数集合CtClass[] paramTypes = {classPool.get(String.class.getName())};// 获取指定方法名称CtMethod method = ctClass.getDeclaredMethod("show", paramTypes);// 赋值方法到新方法中CtMethod newMethod = CtNewMethod.copy(method, ctClass, null);// 修改源方法名称String oldName = method.getName() + "$Impl";method.setName(oldName);// 修改原方法newMethod.setBody("{System.out.println(\"执行前\");" + oldName + "($$);System.out.println(\"执行后\");}");// 将新方法添加到类中ctClass.addMethod(newMethod);// 加载重新编译的类clazz = ctClass.toClass();// 注意,这一行会将类冻结,无法在对字节码进行编辑// 执行方法clazz.getMethod("show", String.class).invoke(clazz.newInstance(), "hello");ctClass.defrost();// 解冻一个类,对应freeze方法}

动态创建类

@Testpublic void test() throws Exception {ClassPool classPool = ClassPool.getDefault();// 创建一个类CtClass ctClass = classPool.makeClass("edu.alvin.reflect.DynamiClass");// 为类型设置接口//ctClass.setInterfaces(new CtClass[] {classPool.get(Runnable.class.getName())});// 为类型设置字段CtField field = new CtField(classPool.get(String.class.getName()), "value", ctClass);field.setModifiers(Modifier.PRIVATE);// 添加getter和setter方法ctClass.addMethod(CtNewMethod.setter("setValue", field));ctClass.addMethod(CtNewMethod.getter("getValue", field));ctClass.addField(field);// 为类设置构造器// 无参构造器CtConstructor constructor = new CtConstructor(null, ctClass);constructor.setModifiers(Modifier.PUBLIC);constructor.setBody("{}");ctClass.addConstructor(constructor);// 参数构造器constructor = new CtConstructor(new CtClass[] {classPool.get(String.class.getName())}, ctClass);constructor.setModifiers(Modifier.PUBLIC);constructor.setBody("{this.value=$1;}");ctClass.addConstructor(constructor);// 为类设置方法CtMethod method = new CtMethod(CtClass.voidType, "run", null, ctClass);method.setModifiers(Modifier.PUBLIC);method.setBody("{System.out.println(\"执行结果\" + this.value);}");ctClass.addMethod(method);// 加载和执行生成的类Class<?> clazz = ctClass.toClass();Object obj = clazz.newInstance();clazz.getMethod("setValue", String.class).invoke(obj, "hello");clazz.getMethod("run").invoke(obj);obj = clazz.getConstructor(String.class).newInstance("OK");clazz.getMethod("run").invoke(obj);}

创建代理类

@Testpublic void test() throws Exception {// 实例化代理类工厂ProxyFactory factory = new ProxyFactory();  //设置父类,ProxyFactory将会动态生成一个类,继承该父类  factory.setSuperclass(TestProxy.class);//设置过滤器,判断哪些方法调用需要被拦截factory.setFilter(new MethodFilter() {  @Override  public boolean isHandled(Method m) {  return m.getName().startsWith("get");}  });Class<?> clazz = factory.createClass();TestProxy proxy = (TestProxy) clazz.newInstance();((ProxyObject)proxy).setHandler(new MethodHandler() {@Overridepublic Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {//拦截后前置处理,改写name属性的内容  //实际情况可根据需求修改  System.out.println(thisMethod.getName() + "被调用");try {Object ret = proceed.invoke(self, args);  System.out.println("返回值: " + ret);return ret;} finally {System.out.println(thisMethod.getName() + "调用完毕");}}});proxy.setName("Alvin");proxy.setValue("1000");proxy.getName();proxy.getValue();}
  其中,TestProxy类内容如下:
public class TestProxy {private String name;private String value;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}}

获取方法名称

@Testpublic void test() throws Exception {// 获取本地类加载器ClassLoader classLoader = getLocaleClassLoader();// 获取要修改的类Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");// 实例化类型池ClassPool classPool = ClassPool.getDefault();classPool.appendClassPath(new ClassClassPath(clazz));CtClass ctClass = classPool.get(clazz.getName());// 获取方法CtMethod method = ctClass.getDeclaredMethod("show", ObjectHelper.argumentsToArray(CtClass.class, classPool.get("java.lang.String")));// 判断是否为静态方法int staticIndex = Modifier.isStatic(method.getModifiers()) ? 0 : 1; // 获取方法的参数MethodInfo methodInfo = method.getMethodInfo();CodeAttribute codeAttribute = methodInfo.getCodeAttribute();LocalVariableAttribute localVariableAttribute = (LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag);for (int i = 0; i < method.getParameterTypes().length; i++) {System.out.println("第" + (i + 1) + "个参数名称为: " + localVariableAttribute.variableName(staticIndex + i));}}
  关于“获取方法名称”,其主要作用是:当Java虚拟机加载.class文件后,会将类方法“去名称化”,即丢弃掉方法形参的参数名,而是用形参的序列号来传递参数。如果要通过Java反射获取参数的参数名,则必须在编辑是指定“保留参数名称”。Javassist则不存在这个问题,对于任意方法,都能正确的获取其参数的参数名。
  Spring MVC就是通过方法参数将请求参数进行注入的,这一点比struts2 MVC要方便很多,Spring也是借助了Javassist来实现这一点的。

附录

  代码中使用了一个ObjectHelper类,这是我自用的一个小工具类,该类的代码可点击查看。

1 0