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

在抽象基类上委托给setter会导致加载时出现VerifyError

董元徽
2023-03-14

这个问题与我之前的问题有关:如何使用Byte Buddy创建默认构造函数

我正在创建一个子类,它在委托方法调用某个实例之前首先设置一些上下文。这已经很好地解决了一个问题。

在加载动态创建的子类时,出现以下错误。

    java.lang.VerifyError: Bad access to protected data in invokevirtual
Exception Details:
  Location:
    com/frequentis/ps/service/test/saga/ProxyTestSaga$ByteBuddy$Rm8DV3Lj.setTimeoutManager(Lcom/codebullets/sagalib/timeout/TimeoutManager;)V @3: invokevirtual
  Reason:
    Type 'com/frequentis/ps/service/test/saga/ProxyTestSaga' (current frame, stack[0]) is not assignable to 'com/frequentis/ps/service/test/saga/ProxyTestSaga$ByteBuddy$Rm8DV3Lj'
  Current Frame:
    bci: @3
    flags: { }
    locals: { 'com/frequentis/ps/service/test/saga/ProxyTestSaga$ByteBuddy$Rm8DV3Lj', 'com/codebullets/sagalib/timeout/TimeoutManager' }
    stack: { 'com/frequentis/ps/service/test/saga/ProxyTestSaga' }
  Bytecode:
    0x0000000: b200 0cb6 0010 57b1                    

    at java.lang.Class.getDeclaredFields0(Native Method)
    at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
    at java.lang.Class.getDeclaredField(Class.java:2068)
    at net.bytebuddy.implementation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:124)
    at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:200)
    at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:200)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.initialize(DynamicType.java:3497)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:3485)
    at com.frequentis.ps.service.test.saga.DynamicSagaTypeBuilder.buildAndLoad(DynamicSagaTypeBuilder.java:65)
    at com.frequentis.ps.service.test.saga.MoreAbstractSpaceSagaUnitTest.generateProxyClassForSagaUnderTest(MoreAbstractSpaceSagaUnitTest.java:274)
    at com.frequentis.ps.service.test.saga.AbstractSpaceSagaUnitTest.enhance(AbstractSpaceSagaUnitTest.java:105)
    at com.frequentis.ps.service.test.saga.ProxyTestSagaTest.before(ProxyTestSagaTest.java:27)

这是目前我的字节好友设置,它适用于几乎所有情况,除了导致显示错误的"setTimeoutManager"和"setState"方法。

// called within the unit test base class (as shown in the call stack above)
builder = new ByteBuddy()
    .subclass(sagaUnderTestClass, ConstructorStrategy.Default.IMITATE_SUPER_TYPE_PUBLIC);
    // define default ctor if necessary that passes "null" values to the super ctor
    builder.method(isAnnotatedWith(StartsSaga.class).or(isAnnotatedWith(EventHandler.class)))
           .intercept(MethodDelegation.to(new ForwardingContextSetupInterceptor<(sagaUnderTest, contextSetter))
                                      .appendParameterBinder(Pipe.Binder.install(Forwarder.class)))
           .method(isPublic()
                           .and(isDeclaredBy(sagaUnderTest.getClass()).or(isDeclaredBy(AbstractSaga.class)
                           .and(not(isAnnotatedWith(StartsSaga.class))).and(not(isAnnotatedWith(EventHandler.class))))
           .intercept(MethodDelegation.to(sagaUnderTest))))

我需要为setter设置不同的设置吗<还是由抽象类引起的
我真的不明白为什么它说对受保护数据的访问不好,是指私有字段吗?

我的类型层次结构是这样的。声明setter的最顶级基类:

public abstract class AbstractSaga<SAGA_STATE extends SagaState> implements Saga<SAGA_STATE>, NeedTimeouts, NeedContext {
    private SAGA_STATE state;
    private boolean completed;
    private TimeoutManager timeoutManager;
    private ExecutionContext context;

    protected AbstractSaga() {
        completed = false;
    }

    // i have omitted some method for clarity

    protected ExecutionContext context() {
        return context;
    }

    @Override
    public SAGA_STATE state() {
        return state;
    }

    @Override
    public void setState(final SAGA_STATE state) {
        this.state = state;
    }

    @Override
    public boolean isFinished() {
        return completed;
    }

    protected void setFinished() {
        completed = true;
    }

    @Override
    public void setTimeoutManager(final TimeoutManager timeoutManager) {
        this.timeoutManager = timeoutManager;
    }
}

通过扩展:

public abstract class AbstractSpaceSaga<SAGA_STATE extends SpaceSagaState, MESSAGE extends Message> 
    extends AbstractSaga<SAGA_STATE> {
}

最后再次扩展为:

public class ProxyTestSaga 
    extends AbstractSpaceSaga<SpaceSagaState, TestRequest> {

    @StartsSaga
    public void handle(final TestRequest request) {
    }

    @EventHandler
    public void handle(final TestEvent event) {
    }
}

我希望代码是可以理解的,我可以添加更多的信息,如果需要任何时间。

共有2个答案

巢海
2023-03-14

嗯,你提供的例子有很多问题,这肯定会让事情变得糟糕。

我觉得你可以更好地研究abstracct类,如何使用它们以及为什么使用它们。

抽象类可以用作子类启动的蓝图。它还可以像一个中心库一样用于类,以获取大量公共字段和方法。抽象类只能包含原始方法。摘要从不初始化。如果需要它进行初始化,则必须将其子类化,并且子类不能初始化,除非它在父抽象类中实现了所有aqbstract方法。从我看到的情况来看,你没有在任何一个扩展中初始化你的方法,因此这些类都没有成为对象。您可以在其中放置getter和setter,但它们无法获取或设置任何内容,直到类的某个扩展最终完成所有功能并成为对象的完整实例。它有点像一个内置的图书馆,直到你做了什么。我用它来保存一个包可能会用到的很多常见的东西,这样我就可以一遍又一遍地获取它们,并在包的其他地方创建更简单的类。

总之,这里的结论是,您的类ProxyTestSaga没有初始化,必须是抽象的,因为它扩展了抽象类AbstractSpace saga,并且没有初始化其所有继承的方法,而您在ProxyTestSage中没有完成初始化。

我几乎不敢回答这个问题,因为你修改了一些代码。然而,我相信你的修订指令意味着原始摘要中有更多从未初始化过的代码。当它到达抽象的最后一个扩展时,就会发生错误。

现在长风已经过去了,我看到你已经在那里声明了覆盖,它们看起来像是要覆盖并为你加载的类提供对其他类的一些可访问性。

您应该能够通过完成所有初始化来解决您的问题。

然而,我看到了一些其他的事情,让我相信你使用它是错误的。看起来你试图保存一组来自其他几个类的方法,以便以后从一个点使用。这不是抽象的目的。你使用一个接口来创建一个包含许多面向外方法的中央存储库。

简单地说,你可以使用一个接口来实现几个方法,抓取这些方法,然后其他类可以使用你的接口来获取ahold并覆盖它们。

还有一件事让我觉得你用错了东西。抽象类只能包含一个类。接口不是类,可以扩展多个类。这是你的代码的另一个问题。我真的不知道为什么你的IDE没有对你大喊大叫。

你应该花几个小时深入阅读接口和摘要及其规则。它们都有自己的怪癖和优势,如果使用得当,都是很好的工具。

麹权
2023-03-14

这绝对是字节伙伴中的一个bug,因为它不允许创建非法字节码。然而,我对正在发生的事情感到困惑,因为字节好友似乎选择了一种与拦截器不兼容的方法。我只是看了一下实现,这是不应该发生的。您使用的是最新版本(0.6.14)吗?

根据字节码,拦截器生成以下代码:

GETSTATIC (interceptor of type ProxyTestSaga)
INVOKEVIRTUAL (some method without arguments)
POP (drop value of the invoked method)
RETURN

调用委托方法时出错。将调用为代理类型定义的方法。从Byte Buddy的代码来看,当使用MethodDelegation时,这是不可能的,所以我假设在某个地方发现了另一个拦截器?

真正对我有帮助的是一个重现错误的可运行示例。也许你可以分解你的代码,这样我就可以运行它。或者,如果你的应用程序是开源的,也许你可以提供一个链接,这样我就可以运行你的代码。或者,和我联系,这样我就可以私下访问你的代码。

最后,您可能希望查看转发指令,而不是使用方法委派进行第二次拦截。除此之外,如果你能帮我解决这个问题,我将不胜感激。

 类似资料:
  • 我有一个简单的自定义ClassLoader 下面是我试图使用加载器的代码: 结果是相当令人期待的: 在大多数文章中,您可以看到,当您在自定义加载程序上调用loadClass时,它首先尝试在其缓存中查找该类。如果找不到,则从父加载程序(在本例中为应用程序加载程序)调用loadClass方法。引导加载程序也是如此。之后,如果引导加载程序在其缓存中找不到该类,它将尝试在相应的源中找到该类。如果加载程序找

  • 问题内容: 有人可以解释何时以及何时不使用Swift中的委托指针进行“弱”分配,为什么? 我的理解是,如果您使用的协议未定义为类,则无法,也不想将您的委托指针分配给弱。 但是,当您的协议定义为类类型协议时,您是否要将委托设置为弱指针? 我对么?在Apple的快速指南中,类协议示例没有使用弱分配,但是在我的测试中,如果没有弱引用我的委托人,我会看到强参考周期。 问题答案: 通常,使类协议(如用关键字

  • 这是我想做的。假设我有这个代码: 我想将Bar中的成员a和b提取到一个单独的Container类中,并以这段代码结束。请注意,Foo不再在bar上调用setA,而是请求容器并在其上调用setter: 在IntelliJ中有没有办法做到这一点? 我可以尝试使用重构- 这不是我想要的。

  • 我有两个对象,分别是和。它们都正确地映射了条令注释和Adresa是一对一的关联。 我可以使用实体管理器分别加载Osoba和Adresa。只要Osoba没有填充属性Adresa,它就可以正常工作。但当我用属性Adresa保存Osoba并试图从数据库检索对象时,抛出了一个错误。 这就是我试图获取对象Osoba的方式。数据库中id为13的Osoba包含指向Adresa表的id。 抛出的错误是 这是我的O

  • 摘要:从正在运行的Java程序加载jar会导致由类间依赖(例如语句)引起的引起的。我如何绕过它? 更详细的问题是: 我试图通过自己的Java程序将jar文件(我们称之为“服务器”)以编程方式加载到Java虚拟机中(我们称之为“ServerAPI”),并使用扩展和其他一些技巧来修改服务器的行为并与之交互。ServerAPI依赖于服务器,但如果服务器不存在,ServerAPI仍然必须能够从网站上运行和

  • 每个闭包都有一个委托对象,当闭包既不是局部变量也不是作为方法参数时,Groovy 使用委托对象查找变量和方法引用. 当委托对象被用来管理时,Gradle 使用它来管理闭包. 例子 13.9.闭包引用 build.gradle dependencies { assert delegate == project.dependencies testCompile('junit:junit