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

Java 在目标而不是代理实例上调用了@Transactional的方法,java,spring,spring-boot,spring-transactions,Java,Spring,Spring Boot,Spring Transactions,我目前正在将我的一个项目从“自配置spring”迁移到spring boot。虽然大部分内容已经在工作,但我对@Transactional方法有一个问题,在调用该方法时,由于调用了“target”实例而不是“proxy”实例(我将尝试在下面详细说明),因此上下文不存在 首先是我的类层次结构的精简视图: @Entity public class Config { // fields and stuff } public interface Exporter { int star

我目前正在将我的一个项目从“自配置spring”迁移到spring boot。虽然大部分内容已经在工作,但我对
@Transactional
方法有一个问题,在调用该方法时,由于调用了“target”实例而不是“proxy”实例(我将尝试在下面详细说明),因此上下文不存在

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

@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 检查这里的参数,我发现
proxy
是id为16585的实例,
target
是id为16606的实例

不幸的是,我没有深入到springs aop的东西,不知道它是否应该是这样的

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

如前所述,该项目已迁移到spring boot,但我们之前已经使用了spring平台bom的
雅典版本。从这里我可以看出,迁移之前没有特殊的AOP配置,迁移之后也没有显式设置值

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

  • 从子类中删除@Scope
  • 将@Transactional从方法级别移动到类
  • 重写子类中的startExport()并将@Transactional放在此处
  • 将@enableSpectJautoproxy添加到应用程序类(我甚至无法登录-没有错误消息)
  • 将spring.aop.proxy-target-class设置为true
  • 以上是不同的组合
目前我没有线索知道如何让它恢复工作

提前谢谢


*希望有人能提供帮助*

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

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


因此,要么删除
final
关键字,以便可以创建CgLib代理,要么显式禁用事务的基于类的代理。Add
@EnableTransationManagement(proxy target class=false)
也应该起作用。除非有其他触发基于类的代理的东西

如果不直接调用该字段,而是在与
startExport()
相同的实例上调用
getConfig()
,会发生什么情况?并返回null。删除
setConfig
上的
final
关键字。已创建代理,但它是基于类的代理。导致在代理上调用
setConfig
,并在代理实例上调用
startExport
。或者将
spring.aop.proxy目标类
切换到
false
以拥有基于接口的代理。@M.Deinum就是这样。。。。我甚至还没有意识到这个方法是最终的。。。幸运的是,我将它复制到问题中,而不是重新键入它。…。@M.Deinum如果您将此作为答案发布,我将接受它。非常感谢!特别是与您的评论相比,更详细的解释! @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. :( } retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();