You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Byte Buddy+Java Agent增强String实现AutoCloseable遇异常求助

问题描述

参考Byte Buddy忽略类相关实现,尝试让java.lang.String实现AutoCloseable接口并重写close方法,但运行时出现异常及VM断开问题,具体情况如下:

尝试的代码

初始AgentBuilder实现

new AgentBuilder.Default()
      .with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
      .ignore(ElementMatchers.none())
      .type((ElementMatchers.named("String")))
      .transform((builder, typeDescription, classLoader, module) -> builder
        .implement(AutoCloseable.class)
        .method(named("close"))
        .intercept(Advice.to(closeInterceptor.class))
      ).installOn(instrumentation);

初始拦截器代码

class closeInterceptor {

  public void intercept(@Origin Class<?> type) {
    try {
      Field field = type.getDeclaredField("value");
      field.setAccessible(true);
      Object ref = field.get(null);
      // 尝试处理java.lang.String的内部字符存储
    } catch (NoSuchFieldException | IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }
}

premain方法代码

public static void premain(String arguments,
  Instrumentation instrumentation) {

  System.out.println("Agent for instrument ignored class like java class ");

  ByteBuddyAgent.install();
  new ByteBuddy()
    .ignore(ElementMatchers.none())
    .redefine(String.class)
    .ignoreAlso(ElementMatchers.none())
    .implement(AutoCloseable.class)
    .method(named("close"))
    .intercept(MethodDelegation.to(closeInterceptor.class))
    .make()
    .load(ClassLoader.getSystemClassLoader())
    .getLoaded();


  new AgentBuilder.Default()
    .with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
    .ignore(ElementMatchers.none())
    .type((ElementMatchers.any()))
    .transform((builder, typeDescription, classLoader, module) -> builder
      .method(ElementMatchers.any())
      .intercept(Advice.to(IgnoreClass.class))
    ).installOn(instrumentation);

}

更新后的拦截器代码

class closeInterceptor {

  private final Logger logger = LoggerFactory.getLogger(closeInterceptor.class);
  public void intercept(@Origin Class<?> type) {
    try {
      Field field = type.getDeclaredField("value");
      field.setAccessible(true);
      Object ref = field.get(null);
      // 尝试处理java.lang.String的内部字符存储
    } catch (NoSuchFieldException | IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }
}

遇到的问题

  1. 拦截器内的日志/输出无任何内容
  2. 运行时出现VM断开错误:Application Server was not connected before run configuration stop, reason: Unable to ping server at localhost:1099,更换端口无效
  3. 抛出绑定异常:
Agent for instrument ignored class like java class 
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
    at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
Caused by: java.lang.IllegalArgumentException: None of [] allows for delegation from public abstract void java.lang.AutoCloseable.close() throws java.lang.Exception
    at net.bytebuddy.implementation.bind.MethodDelegationBinder$Processor.bind(MethodDelegationBinder.java:1026)
    ...
Exception in thread "main" FATAL ERROR in native method: processing of -javaagent failed
Disconnected from the target VM, address: '127.0.0.1:49960', transport: 'socket'
Disconnected from server

本人对字节码操作和Byte Buddy了解有限,希望解决上述异常,成功实现需求。(新年快乐!)


解决方案

1. 修复方法绑定异常

报错核心是AutoCloseable.close()无法与拦截器方法绑定,原因是签名不匹配:

  • AutoCloseable.close()声明抛出Exception,但拦截器方法未声明该异常
  • @Origin Class<?> type参数不符合接口方法实现的绑定规则,应使用@This获取当前String实例

修改后的拦截器代码:

class closeInterceptor {
  private final Logger logger = LoggerFactory.getLogger(closeInterceptor.class);

  public void intercept(@This String stringInstance) throws Exception {
    try {
      Field field = String.class.getDeclaredField("value");
      field.setAccessible(true);
      char[] value = (char[]) field.get(stringInstance);
      // 此处添加字符存储处理逻辑
      logger.info("处理String实例: {}", stringInstance);
    } catch (NoSuchFieldException | IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }
}

2. 修正premain中的错误操作

  • 无需重复调用ByteBuddyAgent.install(),直接使用premain传入的Instrumentation实例即可
  • java.lang.String属于核心系统类,不能用普通redefine方式修改,必须通过AgentBuilder结合Instrumentation操作
  • 全局拦截所有类的所有方法会导致VM崩溃,仅需处理String类即可

修正后的premain代码:

public static void premain(String arguments, Instrumentation instrumentation) {
  System.out.println("Agent started: modifying String to implement AutoCloseable");

  new AgentBuilder.Default()
    // 关闭默认忽略规则,允许修改系统类
    .ignore(ElementMatchers.none())
    // 定位到java.lang.String类
    .type(ElementMatchers.named("java.lang.String"))
    .transform((builder, typeDesc, classLoader, module) -> builder
      // 实现AutoCloseable接口
      .implement(AutoCloseable.class)
      // 将close方法绑定到拦截器
      .method(ElementMatchers.named("close"))
      .intercept(MethodDelegation.to(closeInterceptor.class))
    )
    // 添加控制台错误监听,便于排查问题
    .with(new AgentBuilder.Listener.FallbackToConsole())
    .installOn(instrumentation);
}

3. 解决VM断开问题

VM断开的根源是错误的字节码修改导致VM崩溃,按上述方案修正后即可解决。另外需注意:

  • 运行时添加参数--add-opens java.base/java.lang=ALL-UNNAMED,允许Agent访问String的内部字段
  • 避免用应用服务器配置运行,直接以普通Java应用启动,防止端口冲突

验证方式

编写测试代码验证功能:

public class TestStringClose {
  public static void main(String[] args) {
    try (String testStr = "test") {
      System.out.println("try-with-resources内的字符串: " + testStr);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

运行时添加JavaAgent参数:-javaagent:你的AgentJar路径.jar,同时带上--add-opens java.base/java.lang=ALL-UNNAMED,此时应能看到拦截器的日志输出,且VM正常运行。


内容的提问来源于stack exchange,提问作者luceat-lux-vestra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 20:31:14