如何为ByteBuddy Advice类配置Remapper以解决类型引用重映射问题?
解决ByteBuddy Advice类的Runtime Shading重映射问题
嘿,这个问题我之前踩过坑!ByteBuddy处理Advice类的逻辑确实和常规类不一样——它不是通过类加载器加载Advice类,而是直接读取.class文件的字节流来分析生成增强代码,所以常规的基于类加载阶段的Remapper根本碰不到它。不过咱们有办法手动给Advice类的字节码应用重映射,下面一步步来:
核心思路
既然ByteBuddy是直接读Advice的字节数组,那咱们就先把Advice的字节数组拿出来,手动用ASM的Remapper处理,再把处理后的字节数组喂给ByteBuddy就行。
具体实现步骤
1. 手动重映射Advice类的字节数组
首先要把目标Advice类的字节流读出来,然后用ASM的ClassReader、ClassWriter结合你的自定义Remapper来处理:
// 读取Advice类的原始字节数组(这里用Apache Commons IO简化,也可以自己写流读取逻辑) InputStream adviceStream = YourAdvice.class.getClassLoader() .getResourceAsStream("com/your/package/YourAdvice.class"); byte[] originalAdviceBytes = IOUtils.toByteArray(adviceStream); // 初始化你的自定义Remapper(比如处理类名重映射的实现) Remapper customRemapper = new YourCustomRemapper(); // 用ASM处理字节数组,应用重映射 ClassReader classReader = new ClassReader(originalAdviceBytes); ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES); // 使用RemappingClassVisitor可以一次性处理类、方法、字段的重映射 ClassVisitor remappingVisitor = new RemappingClassVisitor(Opcodes.ASM9, classWriter, customRemapper); classReader.accept(remappingVisitor, ClassReader.EXPAND_FRAMES); byte[] remappedAdviceBytes = classWriter.toByteArray();
2. 让ByteBuddy使用重映射后的Advice字节数组
ByteBuddy允许在Advice.to()方法里指定自定义的ClassFileLocator,咱们把重映射后的字节数组通过这个Locator传给它:
// 创建包含重映射后字节数组的ClassFileLocator ClassFileLocator remappedLocator = ClassFileLocator.Simple.of( TypeDescription.ForLoadedType.of(YourAdvice.class).getName(), remappedAdviceBytes ); // 在ByteBuddy的增强逻辑里使用这个Locator new ByteBuddy() .redefine(TargetClass.class) .visit(Advice.to(YourAdvice.class, remappedLocator) .on(ElementMatchers.named("targetMethod"))) // 匹配你要增强的目标方法 .make() .load(YourAdvice.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);
3. 进阶:批量处理多个Advice类
如果有一堆Advice类要处理,写个自定义的ClassFileLocator来自动处理重映射会更高效:
class RemappingClassFileLocator implements ClassFileLocator { private final ClassFileLocator delegateLocator; private final Remapper remapper; public RemappingClassFileLocator(ClassFileLocator delegate, Remapper remapper) { this.delegateLocator = delegate; this.remapper = remapper; } @Override public Resolution locate(String className) throws IOException { Resolution originalResolution = delegateLocator.locate(className); if (originalResolution.isResolved()) { byte[] originalBytes = originalResolution.resolve(); // 复用前面的重映射逻辑 ClassReader reader = new ClassReader(originalBytes); ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); ClassVisitor visitor = new RemappingClassVisitor(Opcodes.ASM9, writer, remapper); reader.accept(visitor, ClassReader.EXPAND_FRAMES); return Resolution.Simple.of(className, writer.toByteArray()); } return Resolution.Unresolved.INSTANCE; } }
使用的时候直接包装默认的类加载器Locator:
ClassFileLocator defaultLocator = ClassFileLocator.ForClassLoader.of(YourAdvice.class.getClassLoader()); ClassFileLocator autoRemappingLocator = new RemappingClassFileLocator(defaultLocator, customRemapper); // 之后所有Advice都可以用这个Locator Advice.to(AnotherAdvice.class, autoRemappingLocator)
注意事项
- 确保你的
Remapper实现正确处理了所有需要重映射的类型,包括Advice类里引用的字段、方法参数、返回值对应的类名 - ByteBuddy的Advice注解(比如
@Origin、@Argument)不会受重映射影响,因为ByteBuddy是基于字节码中的注解元数据来识别它们的,只要注解本身的类路径正确就行
内容的提问来源于stack exchange,提问作者Nikem
相关产品推荐
相关产品推荐

