美文网首页
工具开发,字节码技术

工具开发,字节码技术

作者: 上山走18398 | 来源:发表于2019-10-27 23:09 被阅读0次

    简介

    几个对比: https://segmentfault.com/a/1190000009956534
    ASM(Automated Storage Management)
    javassist
    动态代理
    cglib(Code Generatator Library)
    BCEL(Byte Code Engineering Library)
    instrument
    jdt-AST
    类加载,探针,ASM,动态代理,instrument,agent

    JavaAgent 是一种可以动态修改java字节码的技术,其实现原理
    内定的方法名是premain
    premain: 启动时,配置javaagent参数来启动
    main
    agentmain: attach方式,在运行过程中动态地设置加载代理类

    public static void premian(String agentOps,Instrumentation inst){
    //在JVM启动时,初始化函数loadClassAndCallPremain方法执行Premain-Class类置顶的premain方法
            inst -> 传入代理实例,操作字节码文件 类加载
    } 
    public static void premian(String agentOps){
    } 
    
    public static void agetmain(){
    //JVM启动后,通过VirtualMachine附着 一个Instrument,如vm.loadAgent(jar),会调用sun.instrument.instrumentationImpl实现类的loadClassAndCallAgentmain方法执行Agentmain-Class指定类的agentmain方法
    
    }
    
    ## Instrument premain、agentmain方法中两个参数agentArgs、inst代表什么,
    1. agentArgs:代理程序命令行中输入参数,同“-javaagnet”一起传入,与main函数不同的是,这个参数是一个字符串而不是一个字符串数组
    2. inst: java.lang.instrumentation实例,由JVM自动传入,集中了几乎所有功能方法,如:类操作,classpath操作等
    

    javaAgent与Java字节码注入技术的Java探针工具

    javaAgent实现原理
    • JVMTI
      JVMTI,是JVM暴露出来给用户扩展使用的接口集合,JVMTI是基于事件驱动JVM级别的AOP java1.6
      JVMTI可以支持第三方工具程序以代理的方式连接和访问JVM,并利用JVMTI提供的丰富编程接口,完成JVM相关功能

    • JVMTIAgent
      JVMTIAgent是一个动态库,利用JVMTI暴露出来的接口实现用户自行的逻辑(idea的调试也是通过这个实现的)
      JVMTIAgent主要有三个方法:

    1. Agent_OnLoad方法,agent在启动时加载,就执行这个方法
    2. Agent_OnAttach方法,agent不是在启动时候加载的,我们先attach到目标线程上,然后对于的目标进程load命令来加载agent
    3. Agent_OnUnload方法,agent卸载时调用
    • java.lang.instrument

    java.lang.instrument中需要关注的是ClassFileTransformer和Instrumentation接口。

    public interface ClassFileTransform{
     byte[] transform(ClassLoader loader,
               String className,
               Class<?> classBeingRedefined,
               ProtectionDomain protectionDomain,
               byte[] classfileBuffer)
               throws IllegalClassFormatException 
    }
    //如果transform方法返回null, 表示我们不对类进行处理直接返回。否则,会用我们返回的byte[]来代替原来的类。
    //也不会生成新的类,也不需要原类的接口
    
    Instrumentation接口。ClassFileTransformer必须添加进Instrumentation才能生效。
    Instrumentation inst;
    ClassFileTransformer classFileTransformer;
    inst.addTransformer(classFileTransformer);
    
    
    META-INF/MANIFEST.MF参数清单
    
    
    • instrument agent

    instrument agent实现了Agent_OnLoad方法和Agent_OnAttach方法

    • JVM attach机制
      jvm attach机制上JVM提供了一种jvm进程间通信的功能,能让一个进程传命令给另一个进程

      1. 比如进行线程dump(程序运行期间,dump指令运行的底层原理,守护进程监听?-jstack -pid等参数传给dump的线程来执行)
    • ClassTransform
      加载类文件的时候发出ClassFileLoad事件,交给Instrument agent来调用 java agent里注册的ClassFileTransformer实现字节码的修改

    • Class Redefine

    参考链接:https://www.cnblog.com/jackion5/p/10680343/html
    最直接改造java类的方法莫过于直接改写class文件

    字节码增强技术框架
    ASM是一个字节码操作框架: Automated Storage Management,需要对class字节码熟悉
    javaassist对字节码修改
    byte buddy

    ASM

    https://www.jianshu.com/p/b5dc9c316f27

    ASM是一个字节码操作框架
    它能被用来动态生成类,或者增强既有类的功能
    ASM可以直接产生二进制class文件,也可以在类被载入Java虚拟机之前动态改变类的行为
    BCELSERL不同, ASM提供了更为现代的模型
    类转换的负载小
    生成的代码可以直接覆盖原来的类,或者是原始类的子类
    案例:lambda表达式,cglib动态代理类
    没有反射带来的性能开销

    Core(各种Visitor):提供了ClassReader 和 ClassWriter
    tree:
    analysis:提供了一个静态字节码分析框架,除了树包之外,还可以使用它来实现真正复杂的类转化,这些转换需要知道每条指令的堆栈映射的状态
    
    ASM字节码操纵的两种方式:
    - CoreApi 访问者模式(Visitor):基于事件驱动
    - TreeAPI 树节点模式:基于面向对象
    
    AOP实现:
    AdviceAdapter是MethodVisitor的子类,使用AdviceAdapter可以更方便的修改方法的字节码
    Opcodes: JVM操作码
    LocalVariablesSorter:对方法的参数&本地局部变量进行重新编号
    GeneratorAdapter:封装了原始字节码操作,例如调用方法时的所有visitMethod封装为各种InvokeXXX
    - OnMethodEnter
    - OnMethodExit
    
    ASM辅助工具
    ASM Bytecode Viewer
    
    Java Instrument

    在整个虚拟上挂一个钩子程序,每次装入一个新类的时候,都必须执行一遍这段程序,即使这个类不要改变
    更适用于监控和控制虚拟机的行为
    但是agentmain,可以动态改变已载入的文件,在程序运行期间做些操作
    Attach APi 后台监听进程开启attach listener(如果未开启,由另外一个线程监听操作)

    动态代理 Proxy类
    接口 -> 实现类 -> 前后之类增加一些额外的操作
    静态代理和动态代理的区别:
      1. 代理类和实现类实现了相同的接口,方法增加,代理类的方法也需要相应增加
      2. 动态代理运用反射机制动态创建而成-> 不用为每一个方法创建代理实现类
      3. 都需要传入被代理对象
    
    
    
    Proxy类,只面向接口,方法
    反射
    java.lang.reflect.InvocationHandler接口
    java.lang.reflect.Proxy
    interface InvocationHadler(){
    
        invoke(...){
    }
    }
    反射引入性能代价
    面向接口编程
    只能改写method
    
    public class dongtaidaili implement InvocationHandler{
    
    targer = 被代理对象 
     
    //只能代理接口,因为newProxyInstance方法只接受接口方法作为参数,最后调用newInstance
    Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this)
      invoke(Object proxy,Method method,Object[] args){ }
    }
    
    
    
    Cglib

    参考链接:https://blog.csdn.net/mulinsen77/article/details/86565891
    利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理
    动态代理和cglib的区别:

    1. 代理对象是目标对象的子类,主要是对指定的类生成一个子类,覆盖其中的方法
    2. 拦截器必须实现MethodInterceptor接口
    3. hibernate中的session.load采用的是cglib实现的
    4. Spring如何选择用jdk还是cglib:
      当bean实现接口时,sprign就会用jdk的动态代理
      当bean没有实现接口时,Spring会使用cglib实现
      可以强制使用cglib
      https://www.cnblogs.com/clds/p/4985893.html
    javasist
    类依赖分析器

    jdeps - java dependencies 、java8开始拥有

    jdeps 命令显示java类文件的包级或类级依赖关系,输入可以是.class文件、目录、jar文件路径名

    Android常见的依赖分析方案

    获得模块与类的关系、类和类之间方法级

    JDT - AST

    https://blog.mythsman.com/post/5d2c11c767f841464434a3bf
    https://segmentfault.com/a/1190000000609246
    https://blog.csdn.net/lovelion/article/details/18953869 AST树描述比较好,完整抽象语法树
    https://www.jianshu.com/p/68027eaf45ad AST NODE 描述
    AST 节点结构 node类型
    ASTNode
    ASTVisitor

    Messager主要是用来在编译期打log用的
    JavacTrees提供了待处理的抽象语法树
    TreeMaker封装了创建AST节点的一些方法
    Names提供了创建标识符的方法

    相关文章

      网友评论

          本文标题:工具开发,字节码技术

          本文链接:https://www.haomeiwen.com/subject/xtmwdctx.html