在目标而不是代理实例上调用 @Transactional 的方法

Method with @Transactional called on target not on proxy instance

我目前正在将我的一个项目从 "self configured spring" 迁移到 spring 引导。虽然大多数东西已经在工作了,但我遇到了一个 @Transactional 方法的问题,当它被调用时,由于调用 "target" 实例而不是 "proxy"实例(我将在下面详细说明)。

首先是我的 class 层次结构的精简视图:

@实体
public class 配置{
    // 字段和东西
}

public接口导出器{

    int startExport() 抛出 ExporterException;

    void setConfig(配置配置);
}


public 摘要 class ExporterImpl 实现 Exporter {
    受保护的配置配置;

    @覆盖
    public final void setConfig(Config 配置) {
        this.config = 配置;
        // this.config 是一个有效的配置实例
    }

    @覆盖
    @Transactional(readOnly = true)
    public int startExport() 抛出 ExporterException {
        // this.config 在这里是 NULL
    }

    // 其他方法,包括 subclass 的抽象方法
}

@Scope("prototype")
@Service("cars2Exporter")
public class Cars2ExporterImpl 扩展 ExporterImpl {

    // 覆盖抽象方法和一些其他的
    // 不接触 startExport()
}

// 还有其他 ExporterImpl 的实现
// 在所有实现中都会出现问题

调用代码是这样的:

@注入
私人供应商<出口商> cars2Exporter;

public void scheduleExport(配置配置){
    出口商出口商 = cars2Exporter.get();
    exporter.setConfig(配置);
    exporter.startExport();
    // 实际上我把它包装在一个 class 实现可运行
    // 并将其放入 `TaskExecutor` 的队列中,但问题发生了
    // 也可以直接调用。 :(
}

到底是什么问题?

在对 startExport() 的调用中,ExporterImpl 的字段 config 为空,尽管它之前已被设置。

到目前为止我发现了什么: 在 exporter.startExport(); 处设置断点,我检查了 eclipse 调试器显示的导出器实例的 ID。在编写此 post 的调试轮中,它是 16585。继续执行到 startExport() 的 call/first 行,在那里我再次检查了 id(这次是 this 的)期望它是相同的但意识到它不是。这里是 16606...所以对 startExport() 的调用是在 class 的另一个实例上完成的...在上一轮调试中我检查了 instance/id调用 setConfig() 将...转到第一个(在本例中为 16585)。这解释了为什么配置字段在 16606 实例中为空。

为了了解我调用 exporter.startExport(); 的行和 startExport() 的实际第一行之间发生了什么,我在 eclipse 调试器中单击了这两行之间的步骤。

我来到 line 655 in CglibAopProxy 看起来像这样:

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

检查这里的参数,我发现 proxy 是 ID 为 16585 的实例,target 是 ID 为 16606 的实例。

不幸的是,我对 spring 的 aop 内容了解不深,不知道它是否应该如此...

我只是想知道为什么有两个实例在不同的方法上被调用。对 setConfig() 的调用转到代理实例,对 startExport() 的调用到达目标实例,因此无法访问先前设置的配置...

如前所述,该项目已迁移到 spring 引导,但我们之前已经使用 Athens-RELEASE 版本的 spring 平台 bom。据我所知,迁移前没有特殊的 AOP 配置,迁移后也没有明确设置值。

为了解决这个问题(或至少以某种方式起作用)我已经尝试了多种方法:

目前我不知道如何让它恢复工作...

提前致谢

*希望有人能帮忙*

Spring Boot 尝试创建一个 cglib 代理,这是一个基于 class 的代理,在您可能拥有基于接口的代理(JDK 动态代理)之前。

因此,您的 Cars2ExporterImpl 的一个子 class 被创建,所有方法都被覆盖,建议将被应用。但是,由于您的 setConfig 方法是 final ,因此无法覆盖该方法,因此实际上将在代理而不是代理实例上调用该方法。

因此,要么删除 final 关键字以便可以创建 CgLib 代理,要么明确禁用基于 class 的交易代理。添加 @EnableTransationManagement(proxy-target-class=false) 也应该可以解决问题。除非有其他东西触发基于 class 的代理。