我正在将我的一个项目从“自配置弹簧”迁移到弹簧启动。虽然大多数东西已经工作了但我遇到了@Transactional
方法的问题,当调用它时,由于调用“目标”实例而不是“代理”实例,因此上下文不存在。 (我将在下面详细说明。)
首先是我的类层次结构的精简视图:
@Entity public class Config { // fields and stuff } public interface Exporter { int startExport() throws ExporterException; void setConfig(Config config); } public abstract class ExporterImpl implements Exporter { protected Config config; @Override public final void setConfig(Config config) { this.config = config; // this.config is a valid config instance here } @Override @Transactional(readOnly = true) public int startExport() throws ExporterException { // this.config is NULL here } // other methods including abstract one for subclass } @Scope("prototype") @Service("cars2Exporter") public class Cars2ExporterImpl extends ExporterImpl { // override abstract methods and some other // not touching startExport() } // there are other implementations of ExporterImpl too // in all implementations the problem occurs
调用代码是这样的:
@Inject private Provider<Exporter> cars2Exporter; public void scheduleExport(Config config) { Exporter exporter = cars2Exporter.get(); exporter.setConfig(config); exporter.startExport(); // actually I'm wrapping it here in a class implementing runnable // and put it in the queue of a `TaskExecutor` but the issue happens // on direct call too. :( }
究竟是什么问题?
在startExport()
的来电中,config
的字段ExporterImpl
为空,尽管它已在之前设置。
到目前为止我发现了什么:
在exporter.startExport();
处有一个断点,我检查了eclipse调试器显示的导出器实例的id。在撰写这篇文章时,在debbug回合中它是16585
。继续执行startExport()
的调用/第一行,我再次检查了id(这次是this
),期望它是相同的但是意识到它不是。这里是16606
...所以对startExport()
的调用是在类的另一个实例上完成的...在之前的调试回合中我检查了实例/ id对{{1}的调用正在......到第一个(在这种情况下是16585)。这解释了为什么16606实例中的配置字段为空。
要理解我调用setConfig()
的行与exporter.startExport();
的实际第一行之间发生了什么,我点击了eclipse调试器中这两行之间的步骤。
我来到line 655 in CglibAopProxy看起来像这样:
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
检查这里的参数我发现startExport()
是id为16585的实例,proxy
是16606的实例。
不幸的是,我并没有那么深入到泉水里去了解它是不是应该如何......
我只是想知道为什么有两个实例可以调用不同的方法。对target
的调用转到代理实例,调用do setConfig()
到达目标实例,因此无法访问先前设置的配置...
如前所述,该项目已迁移到spring boot,但我们之前已经使用了startExport()
版本的spring platform bom。据我所知,迁移前没有特殊的AOP配置,迁移后没有明确设置值。
为了解决这个问题(或者至少在某种程度上工作),我已经尝试过多种方法:
目前,我已经找不到如何恢复工作的线索......
提前致谢
*希望有人可以提供帮助*
答案 0 :(得分:3)
Spring Boot尝试创建一个cglib代理,它是一个基于类的代理,然后你可能有一个基于接口的(JDK动态代理)。
由于这个原因,您的Cars2ExporterImpl
的子类被创建,并且所有方法都被覆盖,并且将应用建议。但是,由于您的setConfig
方法final
无法被覆盖,因此该方法将在代理上实际调用,而不是在代理实例上调用。
因此,请删除final
关键字,以便可以创建CgLib代理或显式禁用事务的基于类的代理。添加@EnableTransationManagement(proxy-target-class=false)
也应该做到这一点。除非有其他东西触发基于类的代理。