【发布时间】:2021-02-18 19:24:46
【问题描述】:
我有一个场景,我需要从未知来源加载类并实例化它们以进行模拟 - 我不需要运行代码,但方法和属性必须在生成的实例中。我还需要类的名称保持不变,因此可以将其实例分配给该类型的其他已加载类的字段。
有时,由于ExceptionInInitializerError,类实例化失败,使类处于无法恢复的无效状态。我不知道哪个类会提前失败。
考虑一下:
class A {
static {
// Throws exception, resulting in 'A' changing to an error state
}
}
class B {
// In case 'A' could not be instantiated properly, I wish to mock
// it so it can be assigned to this field
private A someField;
}
以下是我想出的:
- 使用 ByteBuddy 创建失败类的子类 - 失败并显示
NoClassDefFoundError,可能是因为超类处于错误状态。 - 修改类的静态初始化程序并在使用 ByteBuddy 代理加载时将其包装在 try-catch 语句中 - 以可移植方式完成这似乎相当复杂。
- 在单独的临时类加载器中加载一个类并识别初始化失败;如果已抛出
ExceptionInInitializerError,请重新定义该类并删除其静态初始化程序。这似乎也很难实现,并导致各种链接和循环错误。
我错过了什么吗?有没有更简单的方法来实现我正在寻找的东西?
编辑:
我最终得到了某种解决方案,但是我无法让 Byte Buddy 在没有静态初始化程序的情况下过滤掉类(它将计算每个类的帧)
agentBuilder = agentBuilder.type(ElementMatchers.any())
.transform((builder, type, classLoader, module) -> builder
.visit(new AsmVisitorWrapper.AbstractBase() {
@Override
public int mergeWriter(int flags) {
return flags | ClassWriter.COMPUTE_FRAMES;
}
@Override
public ClassVisitor wrap(TypeDescription instrumentedType, ClassVisitor classVisitor,
Implementation.Context implementationContext, TypePool typePool,
FieldList<FieldDescription.InDefinedShape> fields,
MethodList<?> methods, int writerFlags, int readerFlags) {
if (methods.stream().noneMatch(MethodDescription::isTypeInitializer)) {
return classVisitor;
}
return new ClassVisitor(Opcodes.ASM9, classVisitor) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor,
signature, exceptions);
methodVisitor = new JSRInlinerAdapter(methodVisitor, access,
name, descriptor,
signature, exceptions);
if (name.equals("<clinit>")) {
methodVisitor = new TryCatchBlockSorter(methodVisitor, access, name,
descriptor, signature, exceptions);
methodVisitor = new MethodVisitor(Opcodes.ASM7, methodVisitor) {
private final Label start = new Label();
private final Label end = new Label();
private final Label handler = new Label();
@Override
public void visitCode() {
super.visitCode();
visitTryCatchBlock(start,
end,
handler,
"java/lang/RuntimeException");
visitLabel(start);
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
visitJumpInsn(Opcodes.GOTO, end);
visitLabel(handler);
visitInsn(Opcodes.RETURN);
visitLabel(end);
super.visitMaxs(maxStack, maxLocals);
}
};
}
return methodVisitor;
}
};
}
}));
此外,我在某些类上遇到了以下异常,可能是 Byte Buddy 错误?
[Byte Buddy] ERROR ch.qos.logback.core.util.COWArrayList [ClassLoader@3b00856b, unnamed module @4189d70b, loaded=false]
java.lang.NullPointerException
at net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reducing.onGenericArray(TypeDescription.java:2326)
at net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reducing.onGenericArray(TypeDescription.java:2281)
at net.bytebuddy.description.type.TypeDescription$Generic$OfGenericArray.accept(TypeDescription.java:4415)
at net.bytebuddy.description.method.MethodDescription$Token.asSignatureToken(MethodDescription.java:1915)
at net.bytebuddy.description.method.MethodList$AbstractBase.asSignatureTokenList(MethodList.java:109)
at net.bytebuddy.dynamic.scaffold.inline.RebaseDynamicTypeBuilder.make(RebaseDynamicTypeBuilder.java:227)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:10438)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10374)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1600(AgentBuilder.java:10140)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:10833)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:10771)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10330)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport.transform(Unknown Source)
at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
【问题讨论】:
-
“从未知来源加载类”似乎很冒险。您是在某种沙盒中执行此操作吗?
标签: java reflection classloader byte-buddy