Javassist动态字节码操作库

一. 概述:
Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。相对于bcel, asm等这些工具,开发者不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。简单易用,操作方便。

二. 重要类说明:

  1. ClassPool:javassist的类池,使用ClassPool 类可以跟踪和控制所操作的类,它的工作方式与 JVM 类装载器非常相似
  2. CtClass: CtClass提供了类的操作,如在类中动态添加新字段、方法和构造函数、以及改变类、父类和接口的方法。
  3. CtField:类的属性,通过它可以给类创建新的属性,还可以修改已有的属性的类型,访问修饰符等
  4. CtMethod:类中的方法,通过它可以给类创建新的方法,还可以修改返回类型,访问修饰符等, 甚至还可以修改方法体内容代码
  5. CtConstructor:与CtMethod类似

三. 操作示例

  1. 动态创建新类
private static void createPerson() throws NotFoundException, CannotCompileException, IOException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        ClassPool pool = ClassPool.getDefault();
        //编译时类
        CtClass ctClass = pool.makeClass("com.abc.agent.Person");
        //字段
        CtField field = new CtField(pool.get(String.class.getName()), "name", ctClass);
        //字段访问级别
        field.setModifiers(Modifier.PRIVATE);
        //字段设置默认值
        ctClass.addField(field, CtField.Initializer.constant("Wangwu"));

        //添加setter,getter方法
        ctClass.addMethod(CtNewMethod.setter("setName", field));
        ctClass.addMethod(CtNewMethod.getter("getName", field));

        /**
         * 添加无参构造函数
         */
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, ctClass);
        constructor.setBody("{name=\"test\";}");
        ctClass.addConstructor(constructor);

        /**
         * 添加有参构造函数
         */
        CtConstructor constructor2 = new CtConstructor(new CtClass[]{pool.get(String.class.getName())}, ctClass);
        // $0=this / $1,$2,$3... 代表方法参数
        //详细见:http://www.javassist.org/tutorial/tutorial2.html
        constructor2.setBody("{$0.name=$1;}");
        ctClass.addConstructor(constructor2);

        /**
         * 新增printName方法
         */
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, ctClass);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("{System.out.println(name);}");
        ctClass.addMethod(ctMethod);

        String classPath = "./src/main/java";
        ctClass.writeFile(classPath);


        //调用动态生成的类及方法;
        //方法一:反射调用
        Object instance = ctClass.toClass().newInstance();
        Method setName = instance.getClass().getMethod("setName", String.class);
        setName.invoke(instance, "QiLu-10086");

        Method getName = instance.getClass().getMethod("getName");
        Object invoke = getName.invoke(instance);

        System.out.println(invoke.toString());
    }
// 通过接口方式调用新类:
private static void createInterface() throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException {
        ClassPool pool = ClassPool.getDefault();
        pool.appendClassPath("./src/main/java");

        CtClass IPerson = pool.get("com.abc.agent.IPerson");
        CtClass person = pool.get("com.abc.agent.Person");
        //设置Person类实现了IPerson接口
        try {
            person.setInterfaces(new CtClass[]{IPerson});
        }
        catch (Exception ex){
            ex.printStackTrace();
        }
        IPerson instance = (IPerson)person.toClass().newInstance();
        System.out.println(instance.getName());
        instance.setName("Haizhong");
        instance.printName();
    }

  1. 修改已操作的类
private static void updateMethod() throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get("com.trip.agent.Cat");
        CtMethod method = ctClass.getDeclaredMethod("printCatName");
        method.insertBefore("System.out.println(\"AAAAAAAAAAAAA\");");
        method.insertAfter("System.out.println(\"EEEEEEEEEEEE\");");
        //新增一个方法
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "joinFriend", new CtClass[]{}, ctClass);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("{System.out.println(\"i want to be your friend\");}");
        ctClass.addMethod(ctMethod);

        Object instance = ctClass.toClass().newInstance();
        Method printCatName = instance.getClass().getMethod("printCatName");
        printCatName.invoke(instance);
        System.out.println("printCatName:>>");
        //调用 joinFriend 方法
        Method execute = instance.getClass().getMethod("joinFriend");
        execute.invoke(instance);
        System.out.println("joinFriend:>>");
    }

四. POM文件



    4.0.0

    com.abc.agent
    agent-demo
    1.0-SNAPSHOT

    
        8
        8
        19.0
        4.3.25.RELEASE
        3.25.0-GA
    
    
        
            com.google.guava
            guava
            ${guava.version}
        
        
            org.springframework
            spring-core
            ${spring-core.version}
        
        
            org.javassist
            javassist
            ${javassist.version}
        
        
            org.springframework
            spring-test
            ${spring-core.version}
        
    

    
        
            
                org.apache.maven.plugins
                maven-jar-plugin
                3.1.0
                
                    
                        
                        
                            true
                        
                        
                            com.abc.agent.MyPremain
                            com.abc.agent.MyPremain
                            true
                            true
                        
                    
                    
                    ${project.build.directory}/lib
                    
                        
                        
                        **/*.class
                        **/*.properties
                    
                
            
        
    

六. MANIFEST.MF
在项目的resources目录下依次创建\resources\META-INF\MANIFEST.MF文件, 文件内容如下:
文件内容总共4行, 第4行必须是空行

Manifest-Version: 1.0
Can-Redefine-Classes: true
Premain-Class: com.abc.agent.MyPremain

你可能感兴趣的:(Javassist动态字节码操作库)