为什么@Scheduled注解和@Transaction注解不能一起使用?Spring Boot

13

我有一个问题: 为什么当我们使用@Scheduled@Transaction同时注解一个方法时,事务不起作用? 我知道@Scheduled会调用由Spring创建的代理类而不是我的类本身,但我无法理解这种行为。

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {

    @Override
    @Scheduled(fixedRateString = "${somestring}",initialDelayString = "${anotherstring}")
    @Transactional
    public void doSomething() {

        }
    }

我有两个解决这个问题的方案:

  1. Scheduled方法中调用代理。

  2. 实现ConcurrentTaskScheduler并将ScheduledMethodRunnable对象(即我的类的对象)替换为带有代理的ScheduledMethodRunnable对象。

但是这些解决方案都非常不方便。

你能否解释一下为什么@Scheduled会这样工作吗?

谢谢!


“doesn't work” 是什么意思? - Sotirios Delimanolis
我的意思是,如果调度程序调用了带有@Transactional注释的方法中没有事务,那么实际上并不会开启事务。这是因为调度程序直接调用了我的类中的方法,而不是代理类中的方法。 - Artiom Saidanov
当您删除implements UserService部分时,它可能会起作用,因为那应该会触发不同类型的代理:https://stackoverflow.com/a/30489513/995891 - zapl
2个回答

5
发生这种情况是因为处理两个注释都使用了MAGIC。
我想发生了几件事情:
1. 创建了UserServiceImpl。 2. 处理了@Scheduled注释,并将对bean的引用存储以在适当的时间调用它。 3. 处理了@Transactional注释。创建代理,代理存储对原始bean的引用。在应用程序上下文中,原始bean被替换为代理。
如果步骤2和3按不同的顺序进行,则没有问题。
我不知道如何控制注释处理的顺序。我甚至不确定是否可能。
基本上有两种解决方案。
1. 使用不同类型的MAGIC来处理@Transaction。默认方式是创建代理对象,但也可以指示Spring对当前类进行检测。 2. 将其拆分为两个类,每个类都只有一个注释方法。
例子:
@Service
public class UserServiceImpl implements UserService {

    @Override
    @Transactional
    public void doSomething() {

    }
}

@Service
public class UserServiceScheduler {

    @Inject
    private UserService service;

    @Scheduled(fixedRateString = "${somestring}",initialDelayString = "${anotherstring}")
    public void doSomething() {
         service.doSomething();
    }
}

我个人推荐第二种方法。

我知道那个解决方案,它可以工作。但我也想知道为什么 Spring 会有这样的行为? - Artiom Saidanov
@АртёмСайданов 我添加了一些解释。有关@Transactional如何工作的更多详细信息,您可以轻松地在互联网上找到。 - talex
@talex,我的方法被@Scheduled@Transactional两个注解所标注,我可以看到调度程序正在工作。我在这里使用了cron格式,如下所示:@Override @Scheduled(cron = "0 * * * * ?")。现在我有点困惑,因为它不应该工作,但它确实工作了!任何解释都会很好。谢谢。 - user404

0

问题不在于是私有的还是公共的,而在于:如何调用它以及你使用哪个AOP实现!

如果你使用(默认)Spring代理AOP,那么只有当调用通过代理时,才会考虑Spring提供的所有AOP功能(例如@Transactional)。 - 如果注释方法从另一个bean中调用,则通常情况下是这种情况。

这有两个影响:

  • 因为私有方法不能从另一个bean中调用(例外是反射),所以不考虑它们的@Transactional注释。
  • 如果该方法是公共的,但是从同一个bean中调用它也不会被考虑(仅在使用(默认)Spring代理AOP时才正确)。

您还可以使用AspectJ模式,而不是Spring Proxies,这将解决该问题。AspectJ事务方面甚至编织到私有方法中(适用于Spring 3.0)。

参考:http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/aop.html#aop-proxying


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接