Javassist

作者: 半个橙子 | 来源:发表于2018-06-14 17:22 被阅读0次

    Javassist

    Javassist (Java Programming Assistant) makes Java bytecode manipulation simple. It is a class library for editing bytecodes in Java; it enables Java programs to define a new class at runtime and to modify a class file when the JVM loads it.

    Javassist(Java Programming Assistant)使字节码操作更加简单。它是一个用来编辑Java字节码的类库,可以用来在JVM加载类的时候运行时创建 Java类及修改class文件。其功能与jdk自带的反射功能类似,但比反射功能更强大。

    常用类

    • ClassPool
      是CtClass对象的容器,所有的CtClass都必须从这个对象中加载。使用ClassPool 类可以跟踪和控制所操作的类,它的工作方式与 JVM 类装载器非常相似;

    • CtClass
      一个CtClass代表一个class,它必须从ClassPool 获取。CtClass提供了检查类数据(如字段和方法)以及在类中添加新字段、方法和构造函数、以及改变类、父类和接口的方法。不过,Javassist 并未提供删除类中字段、方法或者构造函数的任何方法;

    • CtField
      用来访问域

    • CtMethod
      用来访问方法

    • CtConstructor
      用来访问构造器

    • toClass()
      这个方法会将这个class转换为java.lang.Class对象。一旦这个方法被调用了,就不再允许对这个class进行其他的修改了。这个方法会使用当前线程上下文中的class loader来加载class。如果程序是在一些应用服务器运行的,那么使用上下文的class loader加载class可能会出现错误。这个方法只是为了方便而提供的,如果需要更复杂的功能那就需要编写自己的class loader。

    依赖javassist

    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.20.0-GA</version>
    </dependency>
    

    示例1 动态创建class

    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.lang.reflect.Modifier;
    import java.net.URL;
    import java.net.URLClassLoader;
    
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtConstructor;
    import javassist.CtField;
    import javassist.CtMethod;
    import javassist.CtNewMethod;
    import org.junit.Before;
    import org.junit.Test;
    
    /**
     * Javassist是一款字节码编辑工具,同时也是一个动态类库,它可以直接检查、修改以及创建 Java类。
     * 以下例子就是创建一个动态类
     * 
     */
    public class CompilerByJavassist {
        public Class createClass() throws Exception{
            // ClassPool:CtClass对象的容器
            ClassPool pool = ClassPool.getDefault();
    
            // 通过ClassPool生成一个public新类Emp.java
            CtClass ctClass = pool.makeClass("com.study.javassist.Emp");
    
            // 添加属性
            // 首先添加属性private String ename
            CtField enameField = new CtField(pool.getCtClass("java.lang.String"),
                    "ename", ctClass);
            enameField.setModifiers(Modifier.PRIVATE);
            ctClass.addField(enameField);
    
            // 其次添加熟悉privtae int eno
            CtField enoField = new CtField(pool.getCtClass("int"), "eno", ctClass);
            enoField.setModifiers(Modifier.PRIVATE);
            ctClass.addField(enoField);
    
            // 为属性ename和eno添加getXXX和setXXX方法
            ctClass.addMethod(CtNewMethod.getter("getEname", enameField));
            ctClass.addMethod(CtNewMethod.setter("setEname", enameField));
            ctClass.addMethod(CtNewMethod.getter("getEno", enoField));
            ctClass.addMethod(CtNewMethod.setter("setEno", enoField));
    
            // 添加构造函数
            CtConstructor ctConstructor = new CtConstructor(new CtClass[] {},
                    ctClass);
            // 为构造函数设置函数体
            StringBuffer buffer = new StringBuffer();
            buffer.append("{\n").append("ename=\"yy\";\n").append("eno=001;\n}");
            ctConstructor.setBody(buffer.toString());
            // 把构造函数添加到新的类中
            ctClass.addConstructor(ctConstructor);
    
            // 添加自定义方法
            CtMethod ctMethod = new CtMethod(CtClass.voidType, "printInfo",
                    new CtClass[] {}, ctClass);
            // 为自定义方法设置修饰符
            ctMethod.setModifiers(Modifier.PUBLIC);
            // 为自定义方法设置函数体
            StringBuffer buffer2 = new StringBuffer();
            buffer2.append("{\nSystem.out.println(\"begin!\");\n")
                    .append("System.out.println(ename);\n")
                    .append("System.out.println(eno);\n")
                    .append("System.out.println(\"over!\");\n").append("}");
            ctMethod.setBody(buffer2.toString());
            ctClass.addMethod(ctMethod);
    
            //最后生成一个class
            Class<?> clazz = ctClass.toClass();
            // 把生成的class文件写入文件
            byte[] byteArr = ctClass.toBytecode();
            FileOutputStream fos = new FileOutputStream(new File("E://Emp.class"));
            fos.write(byteArr);
            fos.close();
            return clazz;
        }
    
    
        /**
         * @Author pengyunlong
         * @Description 动态创建
         * @param
         * @Date 2018/6/15 11:36
         */
        @Test
        public void testInvoke() throws Exception {
            Class<?> aClass = null;
            try {
                aClass = Class.forName("com.study.javassist.Emp");
            }catch (ClassNotFoundException ex){
                aClass = createClass();
            }
            Object obj = aClass.newInstance();
            //反射 执行方法
            obj.getClass().getMethod("printInfo", new Class[] {})
                    .invoke(obj, new Object[] {});
        }
    
        /**
         * @Author pengyunlong
         * @Description 动态修改类,只能修改还未加载的类
         * @param
         * @Date 2018/6/15 11:36
         */
        @Test
        public void testModify() throws Exception {
            CtClass ctClass = ClassPool.getDefault().get("com.soa.other.compiler.Emp");
            CtMethod method = ctClass.getDeclaredMethod("printInfo");
            method.setBody("System.out.println(\"New method!\");");
            ctClass.toClass();
            Emp emp = new Emp();
            emp.printInfo();
        }
    
        @Test
        public void testLoaderDir() throws Exception {
            URLClassLoader urlClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
            for (URL url:urlClassLoader.getURLs()) {
                System.out.println(url.toString());
            }
        }
    }
    
    • 运行testInvoke()
    begin!
    yy
    1
    over!
    
    • 生成字节码到D://Emp.class,用反编译工具打开
    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package com.study.javassist;
    
    public class Emp {
        private String ename = "yy";
        private int eno = 1;
    
        public String getEname() {
            return this.ename;
        }
    
        public void setEname(String var1) {
            this.ename = var1;
        }
    
        public int getEno() {
            return this.eno;
        }
    
        public void setEno(int var1) {
            this.eno = var1;
        }
    
        public Emp() {
        }
    
        public void printInfo() {
            System.out.println("begin!");
            System.out.println(this.ename);
            System.out.println(this.eno);
            System.out.println("over!");
        }
    }
    
    

    示例2 动态修改class

    package com.soa.other.compiler;
    
    public class Student {
        private int age =10;
        private String name="Jack";
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void printInfo(){
            System.out.println("begin!");
            System.out.println(name);
            System.out.println(age);
            System.out.println("over!");
        }
    }
    
    
    package com.soa.other.compiler;
    /**
     * Javassist是一款字节码编辑工具,同时也是一个动态类库,它可以直接检查、修改以及创建 Java类。
     * 以下例子就是创建一个动态类
     * 
     */
    public class CompilerByJavassist {
    
        /**
         * @Author pengyunlong
         * @Description 动态修改类,只能修改还未加载的类
         * @param
         * @Date 2018/6/15 11:36
         */
        @Test
        public void testModify() throws Exception {
            CtClass ctClass = ClassPool.getDefault().get("com.soa.other.compiler.Student");
            CtMethod method = ctClass.getDeclaredMethod("printInfo");
            method.setBody("System.out.println(\"New method!\");");
            ctClass.toClass();
            Student student = new Student();
            student.printInfo();
        }
        
    }
    
    • 输出结果
    New method!
    

    相关文章

      网友评论

          本文标题:Javassist

          本文链接:https://www.haomeiwen.com/subject/twnyeftx.html