zoukankan      html  css  js  c++  java
  • 字节码技术

    Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,可减少冗余代码,提高性能等。

    应用场景

    • AOP技术
    • Lombok去除重复代码插件
    • 利用字节码操作类库动态修改class文件等

    操作步骤

    总原则在内存中获取到原来的字节码,然后通过一些工具(如 ASM,Javaasist)来修改它的byte[]数组,得到一个新的byte数组。
    a、修改字节码
    在JVM加载用户的Class时拦截并修改或者在运行时,使用Instrumentation.redefineClasses方法来替换掉原来的字节码
    b、使字节码生效
    自定义ClassLoader来加载修改后的字节码;

    常用字节码操作类库

    • BCEL---Byte Code Engineering Library(BCEL),这是Apache Software Foundation的Jakarta项目的一部分。BCEL是Java classworking 广泛使用的一种框架,它可以让您深入jvm汇编语言进行类库操作的细节。BCEL与javassist有不同的处理字节码方法,BCEL在实际的jvm指令层次上进行操作(BCEL拥有丰富的jvm指令集支持) 而javassist所强调的是源代码级别的工作。
    • ASM----是一个轻量级Java字节码操作框架,直接涉及到JVM底层的操作和指令
      高性能,高质量CGLB生成类库的底层就是基于ASM实现的
    • javassist是一个开源的分析,编辑和创建Java字节码的类库。性能较ASM差,跟cglib差不多,但是使用简单。很多开源框架都在使用它。
      它的最外层的API和JAVA的反射包中的API颇为类似。
      它主要由CtClass,CtMethod,,以及CtField几个类组成。用以执行和JDK反射API中java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method.Field相同的操作。

    Javassist优势

    a、比反射开销小,性能高。(javassist性能高于反射,低于ASM)
    b、可实现如下功能:
    – 动态生成 新的类
    – 动态改变某个类的结构(添加/删除/修改新的属性/方法)

    Javassist局限性

    a、不支持JDK5.0新语法(包括泛型、枚举)
    b、不支持注解修改,但可以通过底层的javassist类来解决,具体参考:javassist.bytecode.annotation
    c、不支持数组的初始化,如 String[]{"1","2"} ,除非只有数组的容量为 1
    d、不支持内部类和匿名类
    e、不支持 continue 和 break表达式。
    f、对于继承关系,有些不支持。例如
    class A {}
    class B extends A {}
    class C extends B {}

    使用字节码创建文件

    /**
     * 反编译结果:
     * package com.jgspx.entity;
     * public class User
     * {
     *     private String name;
     *     private Integer age;
     *
     *     public String getName() {
     *         return this.name;
     *     }
     *
     *     public User(final String s, final Integer n) {
     *         this.name = this.name;
     *         this.age = this.age;
     *     }
     * }
     *
     */
    public class 使用字节码创建文件 {
        public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException {
            ClassPool pool = ClassPool.getDefault();
            // 1.创建user类
            CtClass userClass = pool.makeClass("com.jarye.entity.User");
            // 2.创建name 和age属性
            CtField nameField = CtField.make(" private String name;", userClass);
            CtField ageField = CtField.make("  private Integer age;", userClass);
            // 3.添加属性
            userClass.addField(nameField);
            userClass.addField(ageField);
            // 4.创建方法
            CtMethod nameMethod = CtMethod.make("public String getName() {return name;}", userClass);
            // 5.添加方法
            userClass.addMethod(nameMethod);
            // 6.添加构造函数
            CtConstructor ctConstructor = new CtConstructor(
                    new CtClass[] { pool.get("java.lang.String"), pool.get("java.lang.Integer") }, userClass);
    
            ctConstructor.setBody("    { this.name = name; this.age = age; }");
            userClass.addConstructor(ctConstructor);
            // 生成class文件
            userClass.writeFile("/Users/jarye/Downloads");
        }
    }
    

    动态修改字节码文件

    /**
     * 代码中原方法:
     * public class User {
     *     private String name;
     *
     *     private Integer aget;
     * }
     *
     * 动态修改后的类信息反编译结果:
     * package com.jgspx.entity;
     *
     * public class User
     * {
     *     private String name;
     *     private Integer aget;
     *
     *     public void sum(final int n, final int n2) {
     *         System.out.println(new StringBuffer().append("sun:").append(n + n2).toString());
     *     }
     * }
     *
     * 执行结果:
     * 开启事务
     * sun:7
     * 提交事务
     *
     */
    public class 动态修改字节码文件 {
        public static void main(String[] args) {
            try {
                ClassPool pool = ClassPool.getDefault();
                // 读取com.jgspx.entity.User
                CtClass userClass = pool.get("com.jarye.entity.User");
                CtMethod method = new CtMethod(CtClass.voidType, "sum", new CtClass[] { CtClass.intType, CtClass.intType },
                        userClass);
                method.setBody("{System.out.println(\"sun:\" + ($1 + $2));}");
                // 添加方法
                userClass.addMethod(method);
                userClass.writeFile("/Users/jarye/Downloads");
                // 动态执行方法
                Class clazz = userClass.toClass();
                Object newInstance = clazz.newInstance();
    
                Method sumMethod = clazz.getDeclaredMethod("sum", int.class, int.class);
                System.out.println("开启事务");
                sumMethod.invoke(newInstance, 2, 5);
                // 使用 javassist 实现动态代理。
                System.out.println("提交事务");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • 相关阅读:
    MQTT:前端js客户端库MQTT.js
    MQTT:java客户端库Paho
    EMQ X:认证
    EMQ X:初体验
    MQTT协议
    连接Mysql时报javax.net.ssl.SSLHandshakeException No appropriate protocol (protocol is disabled or cipher suites are inappropriate)错误
    微星11代gp76 3070解锁140w功率
    Jenkins:使用ssh方式拉取gitlab代码
    juc:AQS
    juc:LockSupport
  • 原文地址:https://www.cnblogs.com/jarye/p/13957945.html
Copyright © 2011-2022 走看看