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();
            }
        }
    }
    
  • 相关阅读:
    Service Name Port Number Transport Protocol tcp udp 端口号16bit
    linux linux 互传文件 win 不通过 ftp sftp 往linux 传文件(文件夹)
    soft deletion Google SRE 保障数据完整性的手段
    Taylor series
    Taylor's theorem
    Moving average
    REQUEST
    Unix file types
    mysqld.sock
    Tunneling protocol
  • 原文地址:https://www.cnblogs.com/jarye/p/13957945.html
Copyright © 2011-2022 走看看