zoukankan      html  css  js  c++  java
  • javassist使用

    http://www.javassist.org/tutorial/tutorial.html    

        Java字节码以二进制的形式存储在.class文件中,每一个.class文件包含一个Java类或接口。

        javassist就是一个用来处理Java字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者修改已有的方法,并且不需要对字节码方面有深入的了解,同时也可以去生成一个新的类对象,通过完全手动的方式

    使用Javassist创建一个class文件

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

    1.创建class文件

    public class CreatePerson {
        public static void  createPerson()throws Exception{
            ClassPool pool = ClassPool.getDefault();
            // 1.创建一个空类
            CtClass cc = pool.makeClass("com.example.javassist.Person");
    
            // 2.新增一个字段private String name;
            CtField param = new CtField(pool.get("java.lang.String"),"name",cc);
            // 访问级别是private
            param.setModifiers(Modifier.PRIVATE);
            // 初始值是 “xiaoming”
            cc.addField(param,CtField.Initializer.constant("xiaoming"));
    
            // 3.生成getter setter方法
            cc.addMethod(CtNewMethod.setter("setName",param));
            cc.addMethod(CtNewMethod.getter("getName",param));
    
            // 4.添加无参的构造函数
            CtConstructor cons = new CtConstructor(new CtClass[]{},cc);
            cons.setBody("{name = "xiaohong";}");
            cc.addConstructor(cons);
    
            // 5.添加有参的构造函数
            cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")},cc);
            cons.setBody("{$0.name = $1;}");
            cc.addConstructor(cons);
    
            // 6.创建一个名为printName方法,无参数,无返回值,输出name值
            CtMethod ctMethod = new CtMethod(CtClass.voidType,"printName",new CtClass[]{},cc);
            ctMethod.setModifiers(Modifier.PUBLIC);
            ctMethod.setBody("{System.out.println(name);}");
            cc.addMethod(ctMethod);
    
            // 这里会将这个创建类的对象编译成.class文件
            cc.writeFile("E:\class");
        }
    
        public static void main(String[] args) {
            try {
                createPerson();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

      生成的文件如下:

    package com.example.javassist;
    
    import java.io.PrintStream;
    
    public class Person
    {
      private String name = "xiaoming";
    
      public void setName(String paramString)
      {
        this.name = paramString;
      }
    
      public void getName(String paramString)
      {
        this.name = paramString;
      }
    
      public Person()
      {
        this.name = "xiaohong";
      }
    
      public Person(String paramString)
      {
        this.name = paramString;
      }
    
      public void printName()
      {
        System.out.println(this.name);
      }
    }
    

     

    在 Javassist 中,类 Javaassit.CtClass 表示 class 文件。一个 GtClass (编译时类)对象可以处理一个 class 文件,ClassPool是 CtClass 对象的容器。它按需读取类文件来构造 CtClass 对象,并且保存 CtClass 对象以便以后使用。
    
    需要注意的是 ClassPool 会在内存中维护所有被它创建过的 CtClass,当 CtClass 数量过多时,会占用大量的内存,API中给出的解决方案是 有意识的调用CtClass的detach()方法以释放内存。
    
    ClassPool需要关注的方法:
    
    getDefault : 返回默认的ClassPool 是单例模式的,一般通过该方法创建我们的ClassPool;
    appendClassPath, insertClassPath : 将一个ClassPath加到类搜索路径的末尾位置 或 插入到起始位置。通常通过该方法写入额外的类搜索路径,以解决多个类加载器环境中找不到类的尴尬;
    toClass : 将修改后的CtClass加载至当前线程的上下文类加载器中,CtClass的toClass方法是通过调用本方法实现。需要注意的是一旦调用该方法,则无法继续修改已经被加载的class;
    get , getCtClass : 根据类路径名获取该类的CtClass对象,用于后续的编辑。
    CtClass需要关注的方法:
    
    freeze : 冻结一个类,使其不可修改;
    isFrozen : 判断一个类是否已被冻结;
    prune : 删除类不必要的属性,以减少内存占用。调用该方法后,许多方法无法将无法正常使用,慎用;
    defrost : 解冻一个类,使其可以被修改。如果事先知道一个类会被defrost, 则禁止调用 prune 方法;
    detach : 将该class从ClassPool中删除;
    writeFile : 根据CtClass生成 .class 文件;
    toClass : 通过类加载器加载该CtClass。
    上面我们创建一个新的方法使用了CtMethod类。CtMthod代表类中的某个方法,可以通过CtClass提供的API获取或者CtNewMethod新建,通过CtMethod对象可以实现对方法的修改。
    
    CtMethod中的一些重要方法:
    
    insertBefore : 在方法的起始位置插入代码;
    insterAfter : 在方法的所有 return 语句前插入代码以确保语句能够被执行,除非遇到exception;
    insertAt : 在指定的位置插入代码;
    setBody : 将方法的内容设置为要写入的代码,当方法被 abstract修饰时,该修饰符被移除;
    make : 创建一个新的方法。
    注意到在上面代码中的:setBody()的时候我们使用了一些符号:
    
    Copy
    // $0=this / $1,$2,$3... 代表方法参数
    cons.setBody("{$0.name = $1;}");
    具体还有很多的符号可以使用,但是不同符号在不同的场景下会有不同的含义,所以在这里就不在赘述,可以看javassist 的说明文档。http://www.javassist.org/tutorial/tutorial2.html 

    2.调用生成的类对象

       1.通过反射调用

    System.out.println(cc.getClass().getName());
    Object person = cc.toClass().newInstance();
    Method name = person.getClass().getMethod("getName");
    Object invoke = name.invoke(person);
    System.out.println(invoke);
    name = person.getClass().getMethod("setName", String.class);
    name.invoke(person,"xiaobai");
    name = person.getClass().getMethod("getName");
    invoke = name.invoke(person);
    System.out.println(invoke);

    name = person.getClass().getMethod("printName");
    name.invoke(person);

      2.通过读取.class文件方式调用

    public static void  executeClass()throws Exception{
            ClassPool pool = ClassPool.getDefault();
            pool.appendClassPath("/E:/class/");
            CtClass ctClass = pool.get("com.example.javassist.Person");
            Object o = ctClass.toClass().newInstance();
            Method method = o.getClass().getMethod("printName");
            method.invoke(o);
        }
    

      3.通过接口的方式

    public interface PersonI {
    
        void setName(String name);
    
        String getName();
    
        void printName();
    
    }
    public static void  interfaceCreate()throws Exception{
            ClassPool pool = ClassPool.getDefault();
            pool.appendClassPath("/E:/class/");
            // 获取接口
            CtClass ctClassI = pool.get("com.example.javassist.PersonI");
            // 获取生成类
            CtClass ctClass = pool.get("com.example.javassist.Person");
            // 使代码生成类,实现PersonI接口
            ctClass.setInterfaces(new CtClass[]{ctClassI});
    
            PersonI o = (PersonI)ctClass.toClass().newInstance();
            o.printName();
        }
    

    3.修改现有的类对象

      常见应用:日志切面 权限切面

    创建测试类

    public class PersonService {
        public void getPerson(){
            System.out.println("getPerson()");
        }
        public void personFly(){
            System.out.println("oh my god, I can fly");
        }
    }
    

      然后对它进行修改

    /**
         * 修改现有类
         */
        public static void updateClss() throws Exception{
            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.get("com.example.javassist.PersonService");
            CtMethod personFly = cc.getDeclaredMethod("personFly");
            personFly.insertBefore("System.out.println("起飞前准备降落伞");");
            personFly.insertAfter("System.out.println("成功落地");");
    
            // 新增一个方法
            CtMethod ctMethod = new CtMethod(CtClass.voidType, "joinFriend", new CtClass[]{}, cc);
            ctMethod.setModifiers(Modifier.PUBLIC);
            ctMethod.setBody("System.out.println("I want to be your friend");");
            cc.addMethod(ctMethod);
    
            Object person = cc.toClass().newInstance();
            // 调用personFly方法
            Method fly = person.getClass().getMethod("personFly");
            fly.invoke(person);
            // 调用joinFriend方法
            Method joinFriend = person.getClass().getMethod("joinFriend");
            joinFriend.invoke(person);
        }
    

      结果

    起飞前准备降落伞
    oh my god, I can fly
    成功落地
    I want to be your friend
    

      

  • 相关阅读:
    973. K Closest Points to Origin
    919. Complete Binary Tree Inserter
    993. Cousins in Binary Tree
    20. Valid Parentheses
    141. Linked List Cycle
    912. Sort an Array
    各种排序方法总结
    509. Fibonacci Number
    374. Guess Number Higher or Lower
    238. Product of Array Except Self java solutions
  • 原文地址:https://www.cnblogs.com/huan30/p/12757896.html
Copyright © 2011-2022 走看看