当前位置: 首页 > 知识库问答 >
问题:

模拟其静态初始值设定项引发ExceptionInInitializerError的类

戚均
2023-03-14

我有一个场景,我需要从未知来源加载类并实例化它们以进行模拟——我不需要运行代码,但方法和属性必须在结果实例中。我还需要类的名称保持不变,以便将其实例分配给其他已加载类的该类型的字段。有时类实例化由于ExceptionInLaunalizerError而失败,使类处于无效状态,无法恢复。我不知道哪个类会事先失败。

考虑一下这一点:

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;
}

以下是我想到的:

  1. 使用ByteBuddy-fails with NoClassDefFoundError创建失败类的子类,这可能是因为超类处于错误状态

我错过什么了吗?有没有更简单的方法来实现我的目标?

我最终找到了某种可行的解决方案,但我无法让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)

共有1个答案

巫马淳
2023-03-14

如果一个类未能加载,它将永远被破坏。您所能做的是,在加载类之前,通过操纵它们的字节码来插入这些类。例如,可以使用Byte-Buddy重新定义类,以完全删除静态初始值设定项。如果使用Mockito另外模拟这些类,那么如果选择模拟它们,它们将无法执行任何代码。

您可以通过添加Java代理或实现自定义的类加载器(在定义之前显式转换任何类)来插入类。

 类似资料:
  • 我通过解决一些黑客等级问题来学习java。下面的代码是关于学习静态初始值设定项块的。例外情况是thown和Capture,但程序仍在运行,我不确定原因。 输入:-1,2 预期输出:java.lang.例外:宽度和高度必须为正 实际输出:宽度和高度必须为正-2

  • 我对Swift类有一个问题。我有UITableViewController类和UITableViewCell类的swift文件。我的问题是UITableViewCell类和网点。这个类有一个错误Class“HomeCell”没有初始化程序,我不明白这个问题。 感谢您的回复。

  • 问题内容: 我对某些文件有引用(在的选项卡中进行配置)。我可以访问和实例化那些文件中包含的类,因此它们包含在类路径中。 我希望这些类将自己注册到全局静态注册表中,因此我向所有这些类添加了一个静态初始化程序块: 我有几个此类都应该注册到的类(这只是一个带有a的类)。但是,每当我访问时,它都不包含任何值。 我读到静态初始值设定项是在首次加载该类时执行的,并且由于我没有在代码中的任何地方直接引用这些类,

  • 我想将一些对象实例绑定到使用Javassist创建的类。该对象是从某个源读取的,数据是预先未知的。 但当我检查API时,Javassist似乎创建了一个真正的字节码,它以“调用这个”或“实例化那个”或“使用这个常量”的形式存储初始化。 有没有办法让Javassist向运行时给定的现有实例添加一个初始化的静态字段?

  • 问题内容: 我似乎无法在有角度的引导程序标签集中设置初始标签。它始终将最左侧的选项卡设置为活动状态。 给定html: 和js: 见柱塞 2013年8月6日更新:现在已在上游修复,请参见github问题。 问题答案: 似乎(静态)选项卡会覆盖运行指令时传递给活动对象的所有内容。我认为这是一个错误。快速又肮脏,您可以使用具有0秒延迟的超时来设置活动状态。至少在插塞中,这不会引起任何闪烁。在您的控制器中

  • 我有2个班: A类: B类: 我创建了一个主类,它只创建了一个新的: 我得到的输出是: 如您所见,在静态初始值设定项之前调用了的构造函数。 我知道这与我创建的循环依赖有关,但我的印象是静态初始化器应该总是在构造函数之前运行。 发生这种情况的原因是什么(技术上在java实现中)? 是否建议一起避免静态初始化器?