添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
豪气的电脑桌  ·  svg绑定点击事件-掘金·  9 月前    · 
< groupId > net . bytebuddy < / groupId > < artifactId > byte - buddy < / artifactId > < version > 1.12 .13 < / version > < / dependency >

方法代理:类似与SpringAOP,创建一个代理对象,去调用目标方法。bytebuddy的方法代理是在目标方法中调用代理类的方法,然后再回调的本类的源方法。

public class UserService {
    public String selectUser(String username) {
        return "张三";
public class AgentMain {
    public static void premain(String agentArgs, Instrumentation instrumentation) {
        AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
            public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
                                                    ClassLoader classLoader, JavaModule module, ProtectionDomain protectionDomain) {
                // 增强方法{selectUser}
                return builder.method(ElementMatchers.named("selectUser"))
                        // 设置拦截器
                        .intercept(MethodDelegation.to(AgentMain.Interceptor.class));
        new AgentBuilder.Default()
                // 忽略掉不增强的类
                .ignore(nameStartsWith("net.bytebuddy."))
                // 增强的类
                .type(ElementMatchers.named("com.liuqi.service.UserService"))
                // 增强的类需 增强的方法实现
                .transform(transformer)
                // 注册增强类监听器
                .with(new AgentBuilder.Listener() {
                    public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {}
                    public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
                        String className = typeDescription.getName();
                        System.out.println("增强类: " + className);
                    public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) {}
                    public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
                        System.out.println("增强类失败: " + typeName + "; 失败原因: " + throwable);
                    public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) { }
                // 监听类加载
                .installOn(instrumentation);
        // 增强后的类, 写入至文件, 便于观察
        instrumentation.addTransformer(new ClassFileTransformer() {
            @Override
            public byte[] transform(ClassLoader loader, String className,
                                    Class<?> classBeingRedefined,
                                    ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                if (className.contains("UserService")) {
                    try {
                        String substring = className.substring(className.lastIndexOf("/"));
                        Files.write(new File("C:/Users/86187/Desktop/bytebuddy-class/" + substring + ".class").toPath(), classfileBuffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                return classfileBuffer;
        });
    public static class Interceptor {
        @RuntimeType
        public static Object intercept(@This Object target,    // 当前拦截的目标对象{this}
                                @AllArguments Object[] allArguments,   // 方法入参
                                @SuperCall Callable<?> superCall,      // 代理对象
                                @Origin Method method                  // 当前拦截的目标方法
        ) throws Throwable {
            Object result = null;  // 方法返回结果
            try {
                System.out.println("前置拦截");
                // 执行目标方法
                result = superCall.call();
                // 返回目标方法执行结果
                return result;
            } catch (Throwable t) {
                System.out.println("异常拦截");
                throw t;
            } finally {
                System.out.println("后置拦截: 方法返回值 = " + result);

在这里插入图片描述
查看增强后的类

可以看到bytebuddy为UserService类又生成了一个类。
在这里插入图片描述
反编译 UserService

标注synthetic说明是编译器生成的。

package com.liuqi.service;
import com.liuqi.agent.AgentMain.Interceptor;
import com.liuqi.service.UserService.auxiliary.VdebFR3h;
import java.lang.reflect.Method;
public class UserService {
   // $FF: synthetic field	
   // 声明方法字段
   private static final Method cachedValue$wCBkyXnP$fi9hdm2;
   // 源目标方法, 字节码已被修改, 调用逻辑为:调用拦截的方法
   public String selectUser(String var1) {
   	  // new VdebFR3h(this, var1) 创建代理对象
      return (String)Interceptor.intercept(
      			this,	// 当前实例对象
       			new Object[]{var1}, // 方法入参
       			new VdebFR3h(this, var1), // 代理对象
       			cachedValue$wCBkyXnP$fi9hdm2	// 拦截方法
   // $FF: synthetic method
   // 源目标方法被重命名, 
   private String selectUser$original$uTSGTdQZ(String username) {
      return "张三";
	// 静态初始化代码块, bytebuddy生成的, 
   static {
      ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.dynamic.Nexus").getMethod("initialize", new Class[]{Class.class, Integer.TYPE}).invoke((Object)null, new Object[]{UserService.class, Integer.valueOf(-449407519)});
      // // 为字段 {Method: cachedValue$wCBkyXnP$fi9hdm2} 赋值
      cachedValue$wCBkyXnP$fi9hdm2 = UserService.class.getMethod("selectUser", new Class[]{String.class});
   // $FF: synthetic method
   // 为代理类提供的调用方法, 取调用目标方法.
   // 因为目标方法被修饰了{private}, 非本类不能访问.
   final String selectUser$original$uTSGTdQZ$accessor$wCBkyXnP(String var1) {
      return this.selectUser$original$uTSGTdQZ(var1);

反编译 UserService import com.liuqi.service.UserService; import java.util.concurrent.Callable; // $FF: synthetic class class UserService$auxiliary$VdebFR3h implements Runnable, Callable { // 目标方法实例 private UserService argument0; // 目标方法入参 private String argument1; // 调用目标方法 public Object call() throws Exception { return this.argument0.selectUser$original$uTSGTdQZ$accessor$wCBkyXnP(this.argument1); // 调用目标方法, 无返回值 public void run() { this.argument0.selectUser$original$uTSGTdQZ$accessor$wCBkyXnP(this.argument1); // 构造函数 UserService$auxiliary$VdebFR3h(UserService var1, String var2) { this.argument0 = var1; this.argument1 = var2;

方法代理 - 可修改入参

自定义一个类。

public interface MyCallable {
     * 定义一个方法, 方法名随意 {此类中只能有这一个方法, 只能有一个入参}
     * @param args  方法入参
     * @return
    Object call(Object[] args);

在设置拦截器时,写入以下配置:

MethodDelegation.withDefaultConfiguration().withBinders(Morph.Binder.install(MyCallable.class))

AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
            public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
                                                    ClassLoader classLoader, JavaModule module, ProtectionDomain protectionDomain) {
                // 增强方法{selectUser}
                return builder.method(ElementMatchers.named("selectUser"))
                        // 设置拦截器
                        .intercept(MethodDelegation.withDefaultConfiguration()  // 写入某些配置
                                    .withBinders(Morph.Binder.install(MyCallable.class))    // 设置一个代理调用类
                                    .to(AgentMain.Interceptor.class));

拦截器修改以下配置:

使用@Morph MyCallable接收代理对象

public static class Interceptor {
        @RuntimeType
        public static Object intercept(@This Object target,    // 当前拦截的目标对象{this}
                                @AllArguments Object[] allArguments,   // 方法入参
                                @Origin Method method,                  // 当前拦截的目标方法
                                @Morph MyCallable superCall             // 代理对象
        ) throws Throwable {
            Object result = null;  // 方法返回结果
            try {
                System.out.println("前置拦截");
                // 修改参数
                allArguments[0] = "1111";
                // 执行目标方法
                result = superCall.call(allArguments);
                // 返回目标方法执行结果
                return result;
            } catch (Throwable t) {
                System.out.println("异常拦截");
                throw t;
            } finally {
                System.out.println("后置拦截: 方法返回值 = " + result);

方法字节码修改

创建拦截器

package com.liuqi.agent;
import net.bytebuddy.asm.Advice;
import java.lang.reflect.Method;
 * @author liuqi
 * @date 2022/12/15 12:27
 * @since 1.0.0
public class AdviceInterceptor {
     * 方法运行前: 执行代码的片段, 必须是静态的方法
    @Advice.OnMethodEnter(suppress = Throwable.class)
    public static void enter(@Advice.This Object that, // 当前实例
                      @Advice.AllArguments Object[] args,   // 方法入参
                      @Advice.Origin Method method)     // 目标方法
        System.out.println("前置拦截 ");
     * 方法退出前, 代码执行片段, 必须是静态的方法
    @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
    public static void exit(@Advice.This Object that, // 当前实例
                     @Advice.AllArguments Object[] args,   // 方法入参
                     @Advice.Origin Method method,   // 目标方法
                     @Advice.Return Object result,  // 返回值
                     @Advice.Thrown Throwable t     // 异常, 如果没有异常, 则 = null
        if (t != null) {
            System.out.println("异常拦截");
        } else {
            System.out.println("后置拦截 ");

设置拦截器

transform方法中填写new AgentBuilder.Transformer.ForAdvice()实例。

advice方法中有两个入参:

  • 参数 0:增强方法匹配器
  • 参数 1:拦截器类名
new AgentBuilder.Default()
                // 忽略掉不增强的类
                .ignore(nameStartsWith("net.bytebuddy."))
                // 增强的类
                .type(ElementMatchers.named("com.liuqi.service.UserService"))
                // 增强的类需 增强的方法实现
                .transform(
                        new AgentBuilder.Transformer.ForAdvice()
                                .advice(ElementMatchers.named("selectUser"), "com.liuqi.agent.AdviceInterceptor")
                // 监听类加载
                .installOn(instrumentation);

查看增强后的类

可以看到bytebuddy没有生成其他的代理类。
在这里插入图片描述
反编译 UserService

可以看到,此增强方式,是将拦截器中的代码直接拷贝至此方法中

package com.liuqi.service;
public class UserService {
   public String selectUser(String var1) {
      try {
         System.out.println("前置拦截 ");
      } catch (Throwable var7) {
      String var2;
      Throwable username;
      label31: {
         String var10000;
         try {
            var10000 = "张三";
         } catch (Throwable var8) {
            username = var8;
            var2 = null;
            break label31;
         var2 = var10000;
         username = null;
      try {
         if(username != null) {
            System.out.println("异常拦截");
         } else {
            System.out.println("后置拦截 ");
      } catch (Throwable var6) {
      if(username != null) {
         throw username;
      } else {
         return var2;
   static {
      ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.dynamic.Nexus").getMethod("initialize", new Class[]{Class.class, Integer.TYPE}).invoke((Object)null, new Object[]{UserService.class, Integer.valueOf(250183171)});
 

方法代理的方式,会为每个方法增强都会生成一个代理对象。例如一个类中,需要增强两个方法,则会生成两个代理类。

skywalking是使用此方式。

在这里插入图片描述
字节码方法修改

此方式只是将代理类的执行代码拷贝至目标方法中,并不会生成额外的代理类。即使一个类中增强了多个方法。

OpenTelemetry使用此方式。

java agent jdk1.5以后引入的字节码插桩技术,可以在代码中加入切点,独立于目标程序,业务侵入性相比于普通的AOP编程要低,可以用作接口的性能检测,参数可性能监控等,常见的微服务链路跟踪的实现原理之一 jdk1.5后新增了类java.lang.instrument.Instrumentation,它提供在运行时重新加载某个类的的class文件的api,部分源码如下: public interface Instrumentation {
mockit使用报错For the inline mock maker, ‘net.bytebuddy:byte-buddy-agent‘ with the module name ‘net.byte
dy-agent 基于Bytebuddy字节码增强技术及Java Agent实现的无侵入式AOP框架 借鉴skywalking的设计原理开发,只保留最基本的match功能 代码简单,容易直接上手二次开发 dy-agent-core:核心功能代码,代码量不大,有兴趣可以瞧一瞧。 dy-agent-log4j:为了不与目标应用的日志框架产生冲突,自己实现的log4j。 如何添加新的拦截器 继承AbstractMethodInterceptor类,需要override两个方法: focusOn 以及 match。 focusOn 在focusOn中定义需要拦截的特定类,目前的matcher有NameMatch和MultiNameOrMatch。可自己实现更多Matcher。 match 对类中的相应方法进行拦截。也可直接返回true,表示拦截所有方法。 参考DemoInter
Java agent是在另一个Java应用程序(“目标”应用程序)启动之前执行的Java程序,为该agent提供修改目标应用程序或其运行环境的机会。在本文中,我们将从基础知识开始,使用字节码操作工具Byte Buddy实现高级代理。 在最基本的用例中,Java agent设置应用程序属性或配置特定的环境状态,从而使代理能够充当可重用和可插入的组件。下面的示例描述了这样一个代理agent,它设置了可供实际程序使用的系统属性: public class Agent { public static vo
1 Byte Buddy Byte Buddy 是一个代码生成和操作库,用于在 Java 应用程序运行时创建和修改 Java 类,而无需编译器的帮助。除了 Java 类库附带的代码生成实用程序外,Byte Buddy 还允许创建任意类,并且不限于实现用于创建运行时代理的接口。此外,Byte Buddy 提供了一种方便的 API,可以使用 Java 代理或在构建过程中手动更改类。
Java agentJava虚拟机(JVM)提供的一种机制,它允许在程序运行时动态地修改或增强Java应用程序的行为。Java agent通常用于在不修改源代码的情况下,对应用程序进行监控、调试、性能分析等操作。 在你提供的信息中,A Java agent has been loaded dynamically (E:\Maven\mavenRepository\net\bytebuddy\byte-buddy-agent\1.14.12\byte-buddy-agent-1.14.12.jar),这表示一个Java agent已经被动态加载到了应用程序中。具体来说,这个Java agent是通过加载位于E:\Maven\mavenRepository\net\bytebuddy\byte-buddy-agent\1.14.12\byte-buddy-agent-1.14.12.jar路径下的byte-buddy-agent-1.14.12.jar文件实现的。 Byte Buddy是一个Java字节码生成和操作库,它可以用于创建和修改Java类的字节码byte-buddy-agent-1.14.12.jar是Byte Buddy库提供的一个Java agent实现,它可以通过动态加载到应用程序中,实现对应用程序的字节码增强和修改。
Me_Liu_Q: 注意下$("#alert_table_div table").find(“tr”)这个配置,这里着重解释下。因为layui会自动生成一个table,所以不能使用 $("#table id")去获取dom, 而是使用("#div table")的方式,在 div 中查找 table。 这个layui自动生成的,可以F12控制台查看表格元素,观察下 layui 动态表格之合并单元格 ht186911: 请问alert_table_div是什么id呢 mybatis-spring底层源码分析 橘子不是唯一的水果O: 表情包非常感谢大佬的博文,为小菜鸡成长的路上提供了很大的帮助表情包 MyBatis 核心类分析 橘子不是唯一的水果O: 大佬的文章让我受益匪浅,如痴如醉,以后的日子还希望能够得到大佬的谆谆指点! sun.misc.Cleaner GC后不回调处理 CSDN-Ada助手: 不知道 Java 技能树是否可以帮到你:https://edu.csdn.net/skill/java?utm_source=AI_act_java