如何用Byte Buddy拦截指定类/包中初始化的ExecutorService方法?
用Byte Buddy拦截特定类/包初始化的ExecutorService实例方法
要实现仅拦截特定类(比如TestClient)或包内初始化的ExecutorService实例方法,核心思路是标记目标来源的实例,再在拦截时只处理带标记的实例。以下是两种可行方案:
方案1:标记工厂方法返回的目标实例
如果你的ExecutorService是通过Executors这类工厂类创建的,可通过拦截工厂方法的调用上下文,给目标类发起的创建请求返回带标记的实例,后续仅拦截带标记的实例方法。
步骤1:定义标记接口
用一个空接口标记目标实例,无需实现逻辑:
public interface TargetExecutorMarker {}
步骤2:拦截工厂方法并标记实例
拦截Executors的静态创建方法,当调用者是目标类/包时,返回子类化并实现标记接口的实例:
public class ExecutorCreationInterceptor { @RuntimeType public static Object intercept(@SuperCall Callable<?> superCall, @CallerClass Class<?> callerClass) throws Exception { Object originalExecutor = superCall.call(); // 仅给目标包/类发起的创建请求标记实例 if (callerClass.getName().startsWith("com.your.target.package") || callerClass.equals(TestClient.class)) { // 通过Byte Buddy子类化原实例并添加标记接口 return new ByteBuddy() .subclass(originalExecutor.getClass()) .implement(TargetExecutorMarker.class) .method(ElementMatchers.any()) .intercept(MethodCall.invokeSelf()) .make() .load(callerClass.getClassLoader()) .getLoaded() .getDeclaredConstructor() .newInstance(); } return originalExecutor; } }
步骤3:拦截ExecutorService方法并过滤标记实例
拦截所有ExecutorService的方法,但仅处理带TargetExecutorMarker标记的实例:
public class ExecutorMethodInterceptor { @RuntimeType public static Object intercept(@This Object instance, @Origin Method method, @AllArguments Object[] args) throws Exception { // 仅处理目标标记的实例 if (instance instanceof TargetExecutorMarker) { // 这里添加你的拦截逻辑:日志、参数修改、耗时统计等 System.out.printf("拦截到目标ExecutorService方法:%s%n", method.getName()); } // 调用原方法逻辑 return method.invoke(instance, args); } }
组装Agent
将上述拦截逻辑整合到Byte Buddy Agent中:
new AgentBuilder.Default() // 拦截Executors的静态创建方法 .type(ElementMatchers.named("java.util.concurrent.Executors")) .transform((builder, typeDesc, classLoader, module) -> builder.method(ElementMatchers.isStatic().and(ElementMatchers.returns(ElementMatchers.isSubTypeOf(ExecutorService.class)))) .intercept(MethodDelegation.to(ExecutorCreationInterceptor.class)) ) // 拦截所有ExecutorService的方法 .type(ElementMatchers.isSubTypeOf(ExecutorService.class)) .transform((builder, typeDesc, classLoader, module) -> builder.method(ElementMatchers.any()) .intercept(MethodDelegation.to(ExecutorMethodInterceptor.class)) ) .installOn(instrumentation);
方案2:追踪目标类持有的ExecutorService实例
如果TestClient直接持有ExecutorService字段,可通过拦截字段赋值来记录目标实例,后续拦截时校验实例是否在记录集合中。
步骤1:实现字段拦截器记录目标实例
public class ExecutorFieldInterceptor { // 线程安全集合存储目标实例 private static final Set<Object> targetExecutors = Collections.synchronizedSet(new HashSet<>()); @RuntimeType public static void setExecutor(@This TestClient client, @Argument(0) ExecutorService executor) { // 记录TestClient持有的ExecutorService实例 targetExecutors.add(executor); } // 提供校验方法 public static boolean isTargetExecutor(Object executor) { return targetExecutors.contains(executor); } }
步骤2:修改ExecutorMethodInterceptor校验实例
public class ExecutorMethodInterceptor { @RuntimeType public static Object intercept(@This Object instance, @Origin Method method, @AllArguments Object[] args) throws Exception { if (ExecutorFieldInterceptor.isTargetExecutor(instance)) { // 执行你的拦截逻辑 System.out.printf("拦截到TestClient持有的ExecutorService方法:%s%n", method.getName()); } return method.invoke(instance, args); } }
组装Agent
new AgentBuilder.Default() // 拦截TestClient的ExecutorService字段赋值 .type(ElementMatchers.named("com.your.package.TestClient")) .transform((builder, typeDesc, classLoader, module) -> builder.field(ElementMatchers.type(ElementMatchers.isSubTypeOf(ExecutorService.class))) .intercept(FieldAccessor.ofField("executor").andThen(FieldDelegation.to(ExecutorFieldInterceptor.class))) ) // 拦截ExecutorService方法 .type(ElementMatchers.isSubTypeOf(ExecutorService.class)) .transform((builder, typeDesc, classLoader, module) -> builder.method(ElementMatchers.any()) .intercept(MethodDelegation.to(ExecutorMethodInterceptor.class)) ) .installOn(instrumentation);
注意事项
- 若遇到不可子类化的
ExecutorService实现(如ForkJoinPool),可改用添加隐藏字段的方式标记实例,避免子类化限制。 - 标记/记录实例的操作需保证线程安全,避免并发场景下的遗漏或误判。
- 若目标类通过多种方式创建
ExecutorService,需覆盖所有创建入口的拦截逻辑。
内容的提问来源于stack exchange,提问作者Avinash Vundyala
相关产品推荐
相关产品推荐

