如果我在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();

当前回答

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,但正如其他人提到的那样,这不能开箱即用。您需要使用AspectJ。我花了一些时间才弄清楚如何让它工作。我将分享我的成果。

我选择使用编译时编织而不是加载时编织,因为我认为这是一个整体上更好的选择。另外,我使用的是Java 8,所以你可能需要调整一些参数。

首先,添加aspectjrt的依赖项。

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.8</version>
</dependency>

然后添加AspectJ插件来在Maven中进行实际的字节码编织(这可能不是一个最小的示例)。

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.8</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
        <aspectLibraries>
            <aspectLibrary>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
        </aspectLibraries>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

最后将其添加到配置类中

@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)

现在您应该能够在私有方法上使用@Transactional。

这种方法需要注意的一点是:您需要将IDE配置为能够识别AspectJ,否则如果您通过Eclipse(例如)运行应用程序,它可能无法工作。确保对直接的Maven构建进行测试,作为完整性检查。

与@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是否重用现有事务,但这段代码肯定会重用。

默认情况下,@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注释可以放在接口定义、接口上的方法、类定义或类上的公共方法之前

你的问题的答案是否定的——@Transactional如果用于注释私有方法将没有任何影响。代理生成器将忽略它们。

Spring Manual第10.5.6章记录了这一点:

方法可见性和@Transactional 在使用代理时,应该应用代理 只有@Transactional注释 到具有公共可见性的方法。如果 注释protected, private或 类的包可见方法 @事务性注释,没有错误 ,但是带注释的方法 没有显示配置 事务设置。考虑到 如果需要,可以使用AspectJ(见下面) 注释非公共方法。