您的位置:首页 > 编程语言 > Java开发

Javassist简单应用总结

2016-06-04 00:10 459 查看
看AOP 实现的方法,了解到可以通过自定义类加载器,加载类的字节码之前使用javassist修改类字节码,直接增强,这种方法比动态代理和cglib多要性能好,但是缺点很明显必须使用自定义的类加载器加载目标类,后面会写关于使用jdk的instrumentation和javassist结合拦截所有的类加载器加载字节码,进行字节码增强

下面是一篇很不错的javassist的应用例子文章:

----------------------------------------------------------------------------------------------------------


概述

  Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果。熟练使用这套工具,可以让Java编程更接近与动态语言编程。

  下面一个方法的目的是获取一个类加载器(ClassLoader),以加载指定的.jar或.class文件,在之后的代码中会使用到。

[java] view
plain copy

 





private static ClassLoader getLocaleClassLoader() throws Exception {  

    List<URL> classPathURLs = new ArrayList<>();  

    // 加载.class文件路径  

    classPathURLs.add(classesPath.toURI().toURL());  

  

    // 获取所有的jar文件  

    File[] jarFiles = libPath.listFiles(new FilenameFilter() {  

        @Override  

        public 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()]));  

}  


获取类型信息

[java] view
plain copy

 





@Test  

public 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();  

}  

修改类方法

[java] view
plain copy

 





@Test  

public 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方法  

}  

动态创建类

[java] view
plain copy

 





@Test  

public 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);  

}  

创建代理类

[java] view
plain copy

 





@Test  

public 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() {  

        @Override  

        public 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类内容如下:

[java] view
plain copy

 





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;  

    }  

}  

获取方法名称

[java] view
plain copy

 





@Test  

public 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类,这是我自用的一个小工具类,该类的代码可点击查看

转自:http://blog.csdn.net/mousebaby808/article/details/37696371
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: