如果我在Spring bean中的私有方法上有一个@Transactional -annotation,那么这个annotation有任何效果吗?

如果@Transactional注释位于公共方法上,则它将工作并打开事务。

public class Bean {
  public void doStuff() {
     doPrivateStuff();
  }
  @Transactional
  private void doPrivateStuff() {

  }
}

...

Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();

当前回答

问题不是私有的或公共的,问题是:如何调用它以及您使用哪个AOP实现!

如果使用(默认)Spring Proxy AOP,那么只有在调用通过代理时才会考虑Spring提供的所有AOP功能(如@Transactional)。—如果从另一个bean调用带注释的方法,通常会出现这种情况。

这有两个含义:

因为不能从另一个bean调用私有方法(例外是反射),所以不会考虑它们的@Transactional Annotation。 如果方法是公共的,但它是从同一个bean调用的,那么它也不会被考虑在内(这句话只在(默认)使用Spring Proxy AOP的情况下是正确的)。

@参见Spring Reference: Chapter 9.6代理机制

恕我直言,您应该使用aspectJ模式,而不是Spring代理,这将克服这个问题。AspectJ事务方面甚至被编织到私有方法中(Spring 3.0检查)。

其他回答

与@loonis建议使用TransactionTemplate的方式相同,可以使用这个帮助组件(Kotlin):

@Component
class TransactionalUtils {
    /**
     * Execute any [block] of code (even private methods)
     * as if it was effectively [Transactional]
     */
    @Transactional
    fun <R> executeAsTransactional(block: () -> R): R {
        return block()
    }
}

用法:

@Service
class SomeService(private val transactionalUtils: TransactionalUtils) {

    fun foo() {
        transactionalUtils.executeAsTransactional { transactionalFoo() }
    }

    private fun transactionalFoo() {
        println("This method is executed within transaction")
    }
}

不知道TransactionTemplate是否重用现有事务,但这段代码肯定会重用。

问题不是私有的或公共的,问题是:如何调用它以及您使用哪个AOP实现!

如果使用(默认)Spring Proxy AOP,那么只有在调用通过代理时才会考虑Spring提供的所有AOP功能(如@Transactional)。—如果从另一个bean调用带注释的方法,通常会出现这种情况。

这有两个含义:

因为不能从另一个bean调用私有方法(例外是反射),所以不会考虑它们的@Transactional Annotation。 如果方法是公共的,但它是从同一个bean调用的,那么它也不会被考虑在内(这句话只在(默认)使用Spring Proxy AOP的情况下是正确的)。

@参见Spring Reference: Chapter 9.6代理机制

恕我直言,您应该使用aspectJ模式,而不是Spring代理,这将克服这个问题。AspectJ事务方面甚至被编织到私有方法中(Spring 3.0检查)。

Spring文档解释了这一点

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. Consider the use of AspectJ mode (see mode attribute in table below) if you expect self-invocations to be wrapped with transactions as well. In this case, there will not be a proxy in the first place; instead, the target class will be weaved (that is, its byte code will be modified) in order to turn @Transactional into runtime behavior on any kind of method.

另一种方法是使用BeanSelfAware

默认情况下,@Transactional属性仅在对从applicationContext获得的引用调用带注释的方法时有效。

public class Bean {
  public void doStuff() {
    doTransactionStuff();
  }
  @Transactional
  public void doTransactionStuff() {

  }
}

这将打开一个事务:

Bean bean = (Bean)appContext.getBean("bean");
bean.doTransactionStuff();

这将不会:

Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();

Spring引用:使用@Transactional

Note: In proxy mode (which is the default), only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with @Transactional! Consider the use of AspectJ mode (see below) if you expect self-invocations to be wrapped with transactions as well. In this case, there won't be a proxy in the first place; instead, the target class will be 'weaved' (i.e. its byte code will be modified) in order to turn @Transactional into runtime behavior on any kind of method.

答案是否定的。请参见Spring参考:使用@Transactional :

@Transactional注释可以放在接口定义、接口上的方法、类定义或类上的公共方法之前