工具开发,字节码技术

简介

几个对比: https://segmentfault.com/a/1190000009956534
ASM
javassist
动态代理
cglib
BCEL
instrument
jdt-AST

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提供了创建标识符的方法
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值