zoukankan      html  css  js  c++  java
  • Javassist 字节码增强技术 使用全解析

    Java中所有的类都被编译为class文件来运行,在编译完class文件之后,类不能再被显示修改,而Javassist就是用来处理编译后的class文件,它可以用来修改方法或者新增方法,并且不需要深入了解字节码,还可以生成一个新的类对象。

    创建class

    创建maven项目,引入Javassist库 po

    <!-- https://mvnrepository.com/artifact/javassist/javassist -->
    <dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.12.1.GA</version>
    </dependency>

    使用javassist来创建一个CodeClass类

    package com.mscloudemsh.zookeeper.test;

    import javassist.*;

    import java.io.IOException;

    public class CreateCodeClass {
    public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException {
    ClassPool pool=ClassPool.getDefault();

    //创建一个空类
    CtClass cc=pool.makeClass("com.mscloudemsh.zookeeper.test.Person");

    //2 新增一个字段
    CtField param=new CtField(pool.get("java.lang.String"),"name",cc);

    //访问级private
    param.setModifiers(Modifier.PRIVATE);
    cc.addField(param);

    //3生成gettersetter方法
    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);
    // $0=this / $1,$2,$3... 代表方法参数
    cons.setBody("{$0.name = $1;}");
    cc.addConstructor(cons);


    CtMethod ctMethod=new CtMethod(CtClass.voidType,"printName",new CtClass[]{},cc);
    ctMethod.setModifiers(Modifier.PUBLIC);
    ctMethod.setBody("{System.out.println(name);}");

    cc.addMethod(ctMethod);

    cc.writeFile("/Users/kevin/Downloads/zookeeper-source-desc/src/main/java");
    }
    }

    执行完之后生成了CodeClass.class

    使用方法

    从上文的demo中可以看到部分使用方法,在javassist中CtClass代表的就是类class,ClassPool就是CtClass的容器,ClassPool维护了所有创建的CtClass对象,需要注意的是当CtClass数量过大会占用大量内存,需要调用CtClass.detach()释放内存。

    ClassPool重点有以下几个方法:
    1. getDefault() 单例获取ClassPool
    2. appendClassPath() 将目录添加到ClassPath
    3. insertClassPath() 在ClassPath插入jar
    4. get() 根据名称获取CtClass对象
    5. toClass() 将CtClass转为Class 一旦被转换则不能修改
    6. makeClass() 创建新的类或接口

    更多移步官方文档:http://www.javassist.org/html/javassist/ClassPool.html

    CtClass需要关注的方法:
    1. addConstructor() 添加构造函数
    2. addField() 添加字段
    3. addInterface() 添加接口
    4. addMethod​() 添加方法
    5. freeze() 冻结类使其不能被修改
    6. defrost() 解冻使其能被修改
    7. detach() 从ClassPool中删除类
    8. toBytecode() 转字节码
    9. toClass() 转Class对象
    10. writeFile() 写入.class文件
    11. setModifiers​() 设置修饰符

    移步:http://www.javassist.org/html/javassist/CtClass.html

    CtMethod继承CtBehavior,需要关注的方法:
    1. insertBefore 在方法的起始位置插入代码
    2. insterAfter 在方法的所有 return 语句前插入代码
    3. insertAt 在指定的位置插入代码
    4. setBody 将方法的内容设置为要写入的代码,当方法被 abstract修饰时,该修饰符被移除
    5. make 创建一个新的方法

    更多移步:http://www.javassist.org/html/javassist/CtBehavior.html

    在setBody()中我们使用了$符号代表参数

    // $0代表this $1代表第一个传入的参数 类推
    printName.setBody("{System.out.println($0.name);}");

    使用CtClass生成对象

    上文我们生成了一个ctClass对象对应的是CodeClass.class,怎么调用CodeClass类生成对象、调用属性或方法?

     采用一种常用的方式

    通过接口调用

    新建一个接口CodeClassI,将CodeClass类的方法全部抽象出来

    package com.mscloudemsh.zookeeper.test;

    import com.mscloudmesh.log.CodeClassI;
    import javassist.*;

    public class InvokerDemo {
    public static void main(String[] args) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException {
    ClassPool pool = ClassPool.getDefault();
    pool.appendClassPath
    ("/Users/kevin/Downloads/zookeeper-source-desc/src/main/java/com/mscloudmesh/log");
    //获取接口
    CtClass codeClassI=pool.get("com.mscloudmesh.log.CodeClassI");
    //获取上面生成的类
    CtClass ctClass=pool.get("com.mscloudmesh.log.CodeClass");
    ctClass.setInterfaces(new CtClass[]{codeClassI});


    //在execute方法前后加入自定义的逻辑
    CtMethod executeMethod = ctClass.getDeclaredMethod("execute");
    executeMethod.insertBefore("System.out.println("--开始");");
    executeMethod.insertAfter( "System.out.println("--结束");");


    //通过接口直接调用
    CodeClassI classI= (CodeClassI) ctClass.toClass().newInstance();
    System.out.println(classI.getName());
    classI.setName("mr kevin");



    classI.execute();
    }
    }
  • 相关阅读:
    超参数(Hyperparameter)
    验证和交叉验证(Validation & Cross Validation)
    训练集,验证集,测试集(以及为什么要使用验证集?)(Training Set, Validation Set, Test Set)
    特征的非线性变换(Feature Non-linear Transformation)
    机器学习---三种线性算法的比较(线性回归,感知机,逻辑回归)(Machine Learning Linear Regression Perceptron Logistic Regression Comparison)
    Numpy练习题
    过拟合产生的原因(Root of Overfitting)
    超哥笔记 ---集群、负载均衡、代理(7)
    搭建前端项目(2)
    超哥笔记 --nginx入门(6)
  • 原文地址:https://www.cnblogs.com/mscm/p/13233685.html
Copyright © 2011-2022 走看看