java--字节码增强--1.2--ByteBuddy--使用
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();}
}
上面的示例代码的作用
- 会创建一个继承至 Object类型的类。
- 类名称:fei.zhou.demo1.business.ByteBuddy2.demo2.DynamicType
- 定义2个字段:
- name:String类型,默认值1
- 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<?> 介绍
- 上节创建的 DynamicType.Unloaded,代表一个尚未加载的类,顾名思义,改类型不会加载到 Java 虚拟机中,它仅仅表示 类的字节码。
- 如果需要将该字节码直接加载到虚拟机使用,你可以通过 ClassLoadingStrategy 来加载。
2.3、DynamicType.Unloaded<?> 常用方法
- getBytes 方法:可以获取到 class文件的字节码。
- saveIn(File) 方法:在File目录,生成一个class文件
- 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 三种字节码增强方式
- subclass
- rebasing
- redefinition
3.1、subclass
- 对应 ByteBuddy.subclass() 方法。
- 为目标类(即被增强的类)生成一个子类,在子类方法中插入动态代码。
3.2、rebasing
- 对应 ByteBuddy.rebasing() 方法。
- 当使用 rebasing 方式增强一个类时,Byte Buddy 保存目标类中所有方法的实现,也就是说,当 Byte Buddy 遇到冲突的字段或方法时,会将原来的字段或方法实现复制到 具有兼容签名的重新命名的私有方法中,而不会抛弃这些字段和方法实现。从而达到不丢失实现的目的。
- 这些重命名的方法 可以继续通过重命名后的名称进行调用。
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
- 对应 ByteBuddy.redefine() 方法。
- 当重定义一个类时,Byte Buddy 可以对一个已有的类添加属性和方法,删除已经存在的方法实现。如果使用其他的方法实现, 去替换已经存在的方法实现,则原来存在的方法实现就会消失。
举例
这里依然是增强 Foo 类的 bar() 方法使其直接返回 “unknow” 字符串,增强结果如下
class Foo { // 增强后的Foo定义String bar() { return "unknow"; }
}
4、类加载策略
4.1、ClassLoadingStrategy.Default
- ClassLoadingStrategy.Default 中 定义了内置的策略
- 如果不指定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、委托函数调用
- 让我们使用字节码编程创建的方法,去调用另外一个方法
- 使用MethodDelegation可以将方法调用委托给任意POJO。
- 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--使用相关推荐
- 干货!Java字节码增强探秘
点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"获取公众号专属群聊入口 来源:美团技术团队 1. 字节码 1.1 什么是字节码? ...
- java 字节码增强原理_深入浅出Java探针技术1--基于java agent的字节码增强案例
Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...
- idea如何反编译字节码指令_美团点评:Java字节码增强技术,线上问题诊断利器...
作者简介:泽恩,美团到店住宿业务研发团队工程师.文章转载于公众号:美团技术团队 1. 字节码 1.1 什么是字节码? Java之所以可以"一次编译,到处运行",一是因为JVM针对各 ...
- Java字节码增强技术
文章目录 字节码 字节码增强 字节码增强技术 字节码 字节码含义:待补充. Java为了能让Java程序编译一次到处运行,用Java 编译器将程序对源代码编译生成固定格式的字节码(.class文件)供 ...
- JAVA字节码增强技术之ASM
ASM是一款基于java字节码层面的代码分析和修改工具:无需提供源代码即可对应用嵌入所需debug代码,用于应用API性能分析,代码优化和代码混淆等工作.ASM的目标是生成,转换和分析已编译的java ...
- python字节码解析_从底层入手,解析字节码增强和Btrace应用
这篇文章聊下字节码和相关的应用. 1.机器码和字节码 机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据. 通常意义上来 ...
- JVM插桩之一:JVM字节码增强技术介绍及入门示例
字节码增强技术:AOP技术其实就是字节码增强技术,JVM提供的动态代理追根究底也是字节码增强技术. 目的:在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修 ...
- java探针 字节码增强_深入浅出Java探针技术1--基于java agent的字节码增强案例
Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...
- 字节码增强:原理与实战
本文由一个拦截器逻辑的使用场景及演变历程,引入字节码增强技术.介绍字节码的本质,字节码增强的原理及JVM 启动过程中的 Agent 加载.生效流程,并对常见字节码操作工具进行了简单应用. 注:本文仅讨 ...
- CGLIB依赖ASM(关于java字节码框架ASM的学习)
本文转自: http://www.cnblogs.com/liuling/archive/2013/05/25/asm.html 一.什么是ASM ASM是一个java字节码操纵框架,它能被用来动态生 ...
最新文章
- 【UIKit】解决iOS7状态栏问题
- 2018全国计算机考试报名入口,北京2018年3月全国计算机等级考试报名入口
- java calendar.add方法_Java Calendar add()方法与示例
- Linux 的系统运行级别
- 12家无人驾驶公司新进展:驾照怕白考了!
- php使用常量cont,php常量介绍
- new blob文件设置编码_前端下载文件amp;下载进度
- vue-element日期框点击不显示,不刷新
- acm康复训练记4-world final 2017
- 网站个人站长会失败的原因
- 得力计算机无法开机,得力针式打印机常见问题及解决方法
- C语言malloc函数的使用!malloc函数的作用:动态内存分配函数,用来向系统请求分配内存空间
- microsoftedge无限弹出_Win10升级之后edge浏览器总是弹出欢迎页面解决方法
- c语言编写函数isprime(int a),用来判断自变量a是否为素数,若是素数,函数返回1,否则返回0。
- 手机游戏的分析,设计,思考
- 解决Xcode couldn‘t find any iOS App Development provisioning profiles matching ‘com.facebook.WebDriver
- Word公式转LaTex神器
- 新库上线 | CnOpenDataA股上市公司财务报表数据
- 办公室VR黄片,骚操作!微软HoloLens之父辞职!
- 什么是跨境电商ERP系统