修改 class_Java 修改类方法的工具与修改时机

44528f4564cff7369890431420c29f42.png

1、织入 vs 代理

织入: 效果相当于修改原类方法里的源代码。 代理: 不对原类的方法或者源代码修改,通过 继承生成子类,或者 引用的方式 来进行功能的增强。

2.修改类行为的工具

2.1织入(通过修改 字节码 来实现)

2.1.1AspectJ

1.编译期织入代码 直接用ajc编译期编译源文件,将源文件(java和aspect文件)编译成class文件。(反编译后的class文件,可以看到织入的源代码)

输入: .java 文件 , aspect文件

工具: ajc

输出: .class文件

2.编译后织入代码 javac 编译后的class文件 作为输入,利用 ajc 向编译后的class文件或者jar 织入切面代码

输入: .class文件

工具: ajc

输出: 织入后的 .class文件 或者jar

3.加载时织入 利用aspectjweaver.jar ,使用 java agent 代理在类加载时期,将切面织入进代码。

输入: .class文件

工具: ajc & java agent

输出: 内存中修改后的的字节码

2.1.2 ASM

ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。   ASM 的 api 常规的有两种使用方式,树形和访问者模式。

输入:无/.class文件

输出:内存中 新生成/修改后的的字节码 、 class文件

2.1.3 javassist

Javaassist 就是一个用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。同时也可以去生成一个新的类对象,通过完全手动的方式。

输入:无/.class文件

输出:内存中 新生成/修改后的的字节码 、 class文件

2.2 通过 代理 来实现

2.2.1 静态代理

使用代理模式,通过代码实现。

2.2.2 动态代理

动态代理类与静态代理类最主要不同的是,代理类的字节码不是在程序运行前生成的,而是在程序运行时再虚拟机中程序自动创建的。

JDK代理

JDK 动态代理用于对接口的代理,动态产生一个实现指定接口的类,注意动态代理有个约束:目标对象一定是要有接口的,没有接口就不能实现动态代理,只能为接口创建动态代理实例,而不能对类创建动态代理。

cglib

CGLib采用了非常底层的字节码技术,其原理是通过目标类的字节码为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。 底层使用字节码处理框架ASM,来转换字节码并生成新的类。

更详细一点说,代理类将目标类作为自己的父类并为其中的每个非final委托方法创建两个方法: 一个是与目标方法签名相同的方法,它在方法中会通过super调用目标方法; 另一个是代理类独有的方法,称之为Callback回调方法,它会判断这个方法是否绑定了拦截器(实现了MethodInterceptor接口的对象),若存在则将调用intercept方法对目标方法进行代理,也就是在前后加上一些增强逻辑。intercept中就会调用上面介绍的签名相同的方法。

3.修改类行为的切入点

3.1 编译期修改

如aspectJ的ajc工具等。 即上图中 1 的部分。

3.2 类加载时修改

类加载时修改类行为的切入点,可以分为 通过 classLoader 修改类行为和通过java agent 修改。 即上图中 2 的部分。

3.2.1 利用classLoader进行修改

ClassLoader在findClass方法中搜索类文件的字节码。找到字节码并将其读入程序后,可以在调用defineClass之前修改它们。例如,在调用defineClass之前,可能会将额外的调试信息添加到类文件中。某些安全应用程序的类文件数据可以加密存储在存储库中;findClass方法可以在调用defineClass之前解密数据。 程序可以动态生成字节码,而不是从存储库中检索它们。 这构成了JSP技术的基础。

3.2.2 利用java agent 的premain 进行修改

在 Java SE 5 当中,开发者可以让 Instrumentation 代理在java的 main 函数运行前执行。

简要说来就是如下几个步骤: 1.编写 premain 函数 public static void premain(String agentArgs, Instrumentation inst);

2.jar 文件打包 3. 运行 java -javaagent:jar 文件的位置 [= 传入 premain 的参数 ]

在这个 premain 函数中,开发者可以进行对类的各种操作。 agentArgs 是 premain 函数得到的程序参数,随同 “– javaagent”一起传入。与 main 函数不同的是,这个参数是一个字符串而不是一个字符串数组,如果程序参数有多个,程序将自行解析这个字符串。 Inst 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入。java.lang.instrument.Instrumentation 是 instrument 包中定义的一个接口,也是这个包的核心部分,集中了其中几乎所有的功能方法,例如类定义的转换和操作等等。

3.3运行时修改

java agentmain

在 Java SE 5 当中,开发者只能在 premain 当中施展想象力,所作的 Instrumentation 也仅限与 main 函数执行前,这样的方式存在一定的局限性。

在 Java SE 5 的基础上,Java SE 6 针对这种状况做出了改进,开发者可以在 main 函数开始执行以后,再启动自己的 Instrumentation 程序。

在 Java SE 6 的 Instrumentation 当中,有一个跟 premain“并驾齐驱”的“agentmain”方法,可以在 main 函数开始运行之后再运行。

参考文档

  1. AspectJ入门 https://www.cnblogs.com/duanxz/p/5194544.html
  2. 原生AspectJ 用法分析 https://blog.mythsman.com/post/5d301cf2976abc05b34546be/
  3. 【译】使用ASM对Java字节码打桩 http://www.dengshenyu.com/java-asm/
  4. 当 Java 字节码遇到 ASM https://zhuanlan.zhihu.com/p/102327062
  5. AOP 的利器:ASM 3.0 介绍 https://developer.ibm.com/zh/articles/j-lo-asm30/
  6. javassist使用全解析 https://www.cnblogs.com/rickiyang/p/11336268.html
  7. CGLib动态代理的底层原理 https://blog.csdn.net/Dustin_CDS/article/details/79685620
  8. Java 动态代理详解 https://laijianfeng.org/2018/12/Java-动态代理详解/
  9. Java ClassLoader详解 https://juejin.im/post/6844903828811153416
  10. Java SE 6 新特性: Instrumentation 新功能 https://www.ibm.com/developerworks/cn/java/j-lo-jse61/index.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值