java–字节码增强–1.2–ByteBuddy–使用


1、快速开始

1.1、依赖

使用bytebuddy 需要引入依赖

<dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy</artifactId><version>1.11.12</version>
</dependency>

1.2、代码

package fei.zhou.demo1.business.ByteBuddy2.demo1;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;public class Test01 {public static void main(String[] args) throws Exception {//创建 class对象Class<?> dynamicType = new ByteBuddy()//指定了新创建类的父类.subclass(Object.class)// 指定生成类的名称.name("fei.zhou.demo1.business.ByteBuddy2.User")// 按名称 拦截该类的 toString()// ElementMatchers.named("toString") : 拦截条件,拦截toString()这个方法, 没有条件,表示所有的方法.method(ElementMatchers.named("toString"))// 指定了拦截到的方法要修改成什么样子.intercept(FixedValue.value("Hello World"))//生成字节码,也就是 动态类型.make()//设置类加载器,加载这个生成的类.load(Test01.class.getClassLoader())//  获得class对象.getLoaded();//Java 反射的API,创建实例Object instance = dynamicType.newInstance();//执行toString 方法String toString = instance.toString();//打印 toString 结果System.out.println(toString);System.out.println(instance.getClass().getCanonicalName());}
}

2、DynamicType.Unloaded

2.1、案例

package fei.zhou.demo1.business.ByteBuddy2.demo2;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;public class Test01 {public static void main(String[] args) throws Exception {//创建了一个动态类型DynamicType.Unloaded<?> dynamicType = new ByteBuddy()//指定了新创建类的父类.subclass(Object.class)//定义类名称.name("fei.zhou.demo1.business.ByteBuddy2.demo2.DynamicType")//增加了两个字段name和age.defineField("name", String.class, 1).defineField("age", Integer.class, 1)// 按名称 拦截该类的 toString().method(ElementMatchers.named("toString"))// 指定了拦截到的方法要修改成什么样子.intercept(FixedValue.value("Hello World!"))//生成字节码,也就是 动态类型.make();}
}

上面的示例代码的作用

  1. 会创建一个继承至 Object类型的类。
  2. 类名称:fei.zhou.demo1.business.ByteBuddy2.demo2.DynamicType
  3. 定义2个字段:
    1. name:String类型,默认值1
    2. age:Integer类型,默认值1

和下面这个类等效

package fei.zhou.demo1.business.ByteBuddy2.demo2;public class DynamicType {String name = "1";Integer age = 1;@Overridepublic String toString() {return "Hello World!";}
}

2.2、DynamicType.Unloaded<?> 介绍

  1. 上节创建的 DynamicType.Unloaded,代表一个尚未加载的类,顾名思义,改类型不会加载到 Java 虚拟机中,它仅仅表示 类的字节码。
  2. 如果需要将该字节码直接加载到虚拟机使用,你可以通过 ClassLoadingStrategy 来加载。

2.3、DynamicType.Unloaded<?> 常用方法

  1. getBytes 方法:可以获取到 class文件的字节码。
  2. saveIn(File) 方法:在File目录,生成一个class文件
  3. inject(File) 方法:将类注入到现有的 Jar 文件中

2.3.1、getBytes 方法


package fei.zhou.demo1.business.ByteBuddy2.demo2;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;import java.io.File;public class Test03 {public static void main(String[] args) throws Exception {//创建了一个动态类型DynamicType.Unloaded<?> dynamicType = new ByteBuddy()//指定了新创建类的父类.subclass(Object.class)//定义类名称.name("fei.zhou.DynamicType2")//增加了两个字段name和age.defineField("name", String.class, 1).defineField("age", Integer.class, 1)// 按名称 拦截该类的 toString().method(ElementMatchers.named("toString"))// 指定了拦截到的方法要修改成什么样子.intercept(FixedValue.value("Hello World!"))//生成字节码,也就是 动态类型.make();// 可以获取到字节码,class文件的字节码byte[] bytes = dynamicType.getBytes();}
}

2.3.2、saveIn(File) 方法

package fei.zhou.demo1.business.ByteBuddy2.demo2;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;import java.io.File;public class Test02 {public static void main(String[] args) throws Exception {//创建了一个动态类型DynamicType.Unloaded<?> dynamicType = new ByteBuddy()//指定了新创建类的父类.subclass(Object.class)//定义类名称.name("fei.zhou.DynamicType2")//增加了两个字段name和age.defineField("name", String.class, 1).defineField("age", Integer.class, 1)// 按名称 拦截该类的 toString().method(ElementMatchers.named("toString"))// 指定了拦截到的方法要修改成什么样子.intercept(FixedValue.value("Hello World!"))//生成字节码,也就是 动态类型.make();// 保存到当前目录File file = new File("D:\\temp");// 通过dynamicType 将字节码保存到文件dynamicType.saveIn(file);}
}


2.3.3、inject(File) 方法


package fei.zhou.demo1.business.ByteBuddy2.demo2;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;import java.io.File;public class Test04 {public static void main(String[] args) throws Exception {//创建了一个动态类型DynamicType.Unloaded<?> dynamicType = new ByteBuddy()//指定了新创建类的父类.subclass(Object.class)//定义类名称.name("fei.zhou.DynamicType2")//增加了两个字段name和age.defineField("name", String.class, 1).defineField("age", Integer.class, 1)// 按名称 拦截该类的 toString().method(ElementMatchers.named("toString"))// 指定了拦截到的方法要修改成什么样子.intercept(FixedValue.value("Hello World!"))//生成字节码,也就是 动态类型.make();// 保存到当前目录File file = new File("D:\\temp\\demo1-1.1.jar");// 将类注入到现有的 Jar 文件中dynamicType.inject(file);}
}

3、ByteBuddy 三种字节码增强方式

  1. subclass
  2. rebasing
  3. redefinition

3.1、subclass

  1. 对应 ByteBuddy.subclass() 方法。
  2. 为目标类(即被增强的类)生成一个子类,在子类方法中插入动态代码。

3.2、rebasing

  1. 对应 ByteBuddy.rebasing() 方法。
  2. 当使用 rebasing 方式增强一个类时,Byte Buddy 保存目标类中所有方法的实现,也就是说,当 Byte Buddy 遇到冲突的字段或方法时,会将原来的字段或方法实现复制到 具有兼容签名的重新命名的私有方法中,而不会抛弃这些字段和方法实现。从而达到不丢失实现的目的。
  3. 这些重命名的方法 可以继续通过重命名后的名称进行调用。
class Foo { // Foo的原始定义String bar() { return "bar"; }
}class Foo { // 增强后的Foo定义String bar() { return "foo" + bar$original(); }// 目标类原有方法private String bar$original() { return "bar"; }
}  

3.3、redefinition

  1. 对应 ByteBuddy.redefine() 方法。
  2. 当重定义一个类时,Byte Buddy 可以对一个已有的类添加属性和方法,删除已经存在的方法实现。如果使用其他的方法实现, 去替换已经存在的方法实现,则原来存在的方法实现就会消失。

举例

这里依然是增强 Foo 类的 bar() 方法使其直接返回 “unknow” 字符串,增强结果如下

class Foo { // 增强后的Foo定义String bar() { return "unknow"; }
}

4、类加载策略

4.1、ClassLoadingStrategy.Default

  1. ClassLoadingStrategy.Default 中 定义了内置的策略
  2. 如果不指定ClassLoadingStrategy,Byte Buffer根据你提供的ClassLoader来推导出一个策略

4.1.1、内置策略如下

WRAPPER 策略:1. 创建一个新的 ClassLoader 来加载动态生成的类型。2. 适合大多数情况,这样生产的动态类不会被ApplicationClassLoader加载到,不会影响到项目中已经存在的类。
CHILD_FIRST 策略:创建一个子类优先加载的 ClassLoader,即打破了双亲委派模型。
INJECTION 策略:使用反射,将动态生成的类型直接注入到当前 ClassLoader 中。

4.2、案例

package fei.zhou.demo1.business.ByteBuddy2.demo3;import fei.zhou.demo1.business.ByteBuddy2.demo2.Test02;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;public class Test01 {public static void main(String[] args) throws Exception {//创建了一个动态类型DynamicType.Unloaded<?> dynamicType = new ByteBuddy()//指定了新创建类的父类.subclass(Object.class)//定义类名称.name("User")//生成字节码,也就是 动态类型.make();Class<?> classObject = dynamicType.load(Test02.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)//  获得class对象.getLoaded();}
}

5、动态定义字节码字段,方法和参数

package fei.zhou.demo1.business.ByteBuddy2.demo4;public class Foo {
}
package fei.zhou.demo1.business.ByteBuddy2.demo4;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;import java.io.File;
import java.lang.reflect.Modifier;public class Test01 {public static void main(String[] args) throws Exception {DynamicType.Unloaded<Foo> dynamicType = new ByteBuddy()// 指定父类.subclass(Foo.class)//定义类名称.name("FooSon")//定义方法;名称、返回类型、属性public static void.defineMethod("say", String.class, Modifier.PUBLIC)//定义参数;参数类型、参数名称.withParameter(String.class, "sayStr")// 指定了拦截到的方法要修改成什么样子.intercept(FixedValue.value("hello world"))// 新增一个字段,该字段名称成为"name",类型是 String,且public修饰.defineField("name", String.class, Modifier.PUBLIC).defineField("age", Integer.class, Modifier.PRIVATE)// 产生字节码.make();// 保存到当前目录File file = new File("D:\\temp");// 通过dynamicType 将字节码保存到文件dynamicType.saveIn(file);}
}

6、委托函数调用

  1. 让我们使用字节码编程创建的方法,去调用另外一个方法
  2. 使用MethodDelegation可以将方法调用委托给任意POJO。
  3. Byte Buddy不要求Source(被委托类)、Target类的方法名一致
# 在intercept方法中,使用 MethodDelegation.to 委托到 DelegateClazz的静态方法
intercept(MethodDelegation.to(DelegateClazz.class))  # 在intercept方法中,使用 MethodDelegation.to 委托到 DelegateClazz的 成员方法
intercept(MethodDelegation.to(new DelegateClazz())

6.1、静态方法

 public class Foo {public static String getInfo() {return "Foo";}
}
package fei.zhou.demo1.business.ByteBuddy2.demo5;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;import java.io.File;
import java.lang.reflect.Modifier;public class Test01 {public static void main(String[] args) throws Exception {DynamicType.Unloaded<Foo> dynamicType = new ByteBuddy().subclass(Foo.class).name("User4")//定义方法getInfo2.defineMethod("getInfo2", String.class, Modifier.PUBLIC + Modifier.STATIC)//调用的方法,使用MethodDelegation进行委托.intercept(MethodDelegation.to(Foo.class)).make();// 动态增强生成的字节码类System.out.println("生成的class名称:" + dynamicType.load(ByteBuddy.class.getClassLoader()).getLoaded().getName());// 保存到当前目录File file = new File("D:\\temp");// 通过dynamicType 将字节码保存到文件dynamicType.saveIn(file);}
}

6.2、动态方法

package fei.zhou.demo1.business.ByteBuddy2.demo5;public class Foo2 {public String getInfo() {return "Foo2";}
}
package fei.zhou.demo1.business.ByteBuddy2.demo5;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;import java.io.File;
import java.lang.reflect.Modifier;public class Test02 {public static void main(String[] args) throws Exception {Foo2 foo2 = new Foo2();DynamicType.Unloaded<Foo2> dynamicType = new ByteBuddy().subclass(Foo2.class).name("User5")//定义方法getInfo2.defineMethod("getInfo2", String.class, Modifier.PUBLIC + Modifier.STATIC)//调用的方法,使用MethodDelegation进行委托.intercept(MethodDelegation.to(new Foo2())).make();// 保存到当前目录File file = new File("D:\\temp");// 通过dynamicType 将字节码保存到文件dynamicType.saveIn(file);Class<? extends Foo2> peopleClass = dynamicType.load(ByteBuddy.class.getClassLoader()).getLoaded();Object result = peopleClass.getMethod("getInfo").invoke(peopleClass.newInstance());System.out.println("结果:" + result);}}输出:结果:Foo2

6.3、委托原则

遵循一个最接近原则

6.3.1、案例

public class Source {public String hello(String name) {return "Source " + name;}
}
package fei.zhou.demo1.business.ByteBuddy2.demo6;public class Target {public static String say(String name) {return "Target " + name + "!";}public static String say(int i) {return Integer.toString(i);}public static String say(Object o) {return o.toString();}}
package fei.zhou.demo1.business.ByteBuddy2.demo6;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;import java.io.File;
import java.lang.reflect.Method;import static net.bytebuddy.matcher.ElementMatchers.named;public class Test05 {public static void main(String[] args) throws Exception {//创建了一个动态类型DynamicType.Unloaded<Source> dynamicType = new ByteBuddy()//指定了新创建类的父类.subclass(Source.class).name("Source2")// 按名称 拦截该类的 hello().method(named("hello"))//调用的方法,使用MethodDelegation进行委托.intercept(MethodDelegation.to(Target.class))//生成字节码,由类加载器加载到虚拟机中.make();Class<? extends Source> source2Class = dynamicType.load(ByteBuddy.class.getClassLoader()).getLoaded();Method helloMethod = source2Class.getMethod("hello", String.class);Object result = helloMethod.invoke(source2Class.newInstance(), "我是");System.out.println("结果:" + result);// 保存到当前目录File file = new File("D:\\temp");// 通过dynamicType 将字节码保存到文件dynamicType.saveIn(file);}
}输出:
结果:Target 我是!

6.3.2、说明

Byte Buddy遵循一个最接近原则:

  • say(int)因为参数类型不匹配,直接Pass
  • 另外两个方法参数都匹配,但是 say(String)类型更加接近,因此会委托给它

java–字节码增强–1.2–ByteBuddy–使用


7、拦截方法

package fei.zhou.demo1.business.ByteBuddy2.demo7;public class Foo {public String foo1() {return "foo1";}public String foo2(int i) {return "foo2";}public String foo3(int i) {return "foo3";}public String foo3(Object i,String s) {return "foo4";}
}
package fei.zhou.demo1.business.ByteBuddy2.demo7;import fei.zhou.demo1.business.ByteBuddy2.demo6.Source;
import fei.zhou.demo1.business.ByteBuddy2.demo6.Target;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;import java.io.File;import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;public class Test01 {public static void main(String[] args) throws Exception {//创建了一个动态类型DynamicType.Unloaded<Foo> dynamicType = new ByteBuddy()//指定了新创建类的父类.subclass(Foo.class).name("Foo2")// 拦截  由Foo.class声明的 所有方法.method(isDeclaredBy(Foo.class))// 指定了拦截到的方法要修改成什么样子.intercept(FixedValue.value("1111"))// 拦截  为foo3,入参数量为1的方法.method(named("foo3").and(takesArguments(1))).intercept(FixedValue.value("2222"))//生成字节码.make();// 保存到当前目录File file = new File("D:\\temp");// 通过dynamicType 将字节码保存到文件dynamicType.saveIn(file);}
}

java--字节码增强--1.2--ByteBuddy--使用相关推荐

  1. 干货!Java字节码增强探秘

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"获取公众号专属群聊入口 来源:美团技术团队 1. 字节码 1.1 什么是字节码? ...

  2. java 字节码增强原理_深入浅出Java探针技术1--基于java agent的字节码增强案例

    Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...

  3. idea如何反编译字节码指令_美团点评:Java字节码增强技术,线上问题诊断利器...

    作者简介:泽恩,美团到店住宿业务研发团队工程师.文章转载于公众号:美团技术团队 1. 字节码 1.1 什么是字节码? Java之所以可以"一次编译,到处运行",一是因为JVM针对各 ...

  4. Java字节码增强技术

    文章目录 字节码 字节码增强 字节码增强技术 字节码 字节码含义:待补充. Java为了能让Java程序编译一次到处运行,用Java 编译器将程序对源代码编译生成固定格式的字节码(.class文件)供 ...

  5. JAVA字节码增强技术之ASM

    ASM是一款基于java字节码层面的代码分析和修改工具:无需提供源代码即可对应用嵌入所需debug代码,用于应用API性能分析,代码优化和代码混淆等工作.ASM的目标是生成,转换和分析已编译的java ...

  6. python字节码解析_从底层入手,解析字节码增强和Btrace应用

    这篇文章聊下字节码和相关的应用. 1.机器码和字节码 机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据. 通常意义上来 ...

  7. JVM插桩之一:JVM字节码增强技术介绍及入门示例

    字节码增强技术:AOP技术其实就是字节码增强技术,JVM提供的动态代理追根究底也是字节码增强技术. 目的:在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修 ...

  8. java探针 字节码增强_深入浅出Java探针技术1--基于java agent的字节码增强案例

    Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...

  9. 字节码增强:原理与实战

    本文由一个拦截器逻辑的使用场景及演变历程,引入字节码增强技术.介绍字节码的本质,字节码增强的原理及JVM 启动过程中的 Agent 加载.生效流程,并对常见字节码操作工具进行了简单应用. 注:本文仅讨 ...

  10. CGLIB依赖ASM(关于java字节码框架ASM的学习)

    本文转自: http://www.cnblogs.com/liuling/archive/2013/05/25/asm.html 一.什么是ASM ASM是一个java字节码操纵框架,它能被用来动态生 ...

最新文章

  1. 【UIKit】解决iOS7状态栏问题
  2. 2018全国计算机考试报名入口,北京2018年3月全国计算机等级考试报名入口
  3. java calendar.add方法_Java Calendar add()方法与示例
  4. Linux 的系统运行级别
  5. 12家无人驾驶公司新进展:驾照怕白考了!
  6. php使用常量cont,php常量介绍
  7. new blob文件设置编码_前端下载文件amp;下载进度
  8. vue-element日期框点击不显示,不刷新
  9. acm康复训练记4-world final 2017
  10. 网站个人站长会失败的原因
  11. 得力计算机无法开机,得力针式打印机常见问题及解决方法
  12. C语言malloc函数的使用!malloc函数的作用:动态内存分配函数,用来向系统请求分配内存空间
  13. microsoftedge无限弹出_Win10升级之后edge浏览器总是弹出欢迎页面解决方法
  14. c语言编写函数isprime(int a),用来判断自变量a是否为素数,若是素数,函数返回1,否则返回0。
  15. 手机游戏的分析,设计,思考
  16. 解决Xcode couldn‘t find any iOS App Development provisioning profiles matching ‘com.facebook.WebDriver
  17. Word公式转LaTex神器
  18. 新库上线 | CnOpenDataA股上市公司财务报表数据
  19. 办公室VR黄片,骚操作!微软HoloLens之父辞职!
  20. 什么是跨境电商ERP系统

热门文章

  1. Allegro 16 板内挖孔
  2. android8 .te文件,te文件
  3. 花儿为什么这样红(竹笛恋曲)铃声 花儿为什么这样红(竹笛恋曲)...
  4. MyBatisPlus(个人笔记)
  5. Markdown语法005:分割线
  6. 【ONE·C++ || 哈希(二)】
  7. 游戏是怎样炼成的? 本田太郎
  8. Tabby 终端试用
  9. spark core、spark sql、spark streaming 联系与区别
  10. 编写西门子300plc控制三菱伺服电机机