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

当前回答

如果需要在事务中包装私有方法,而不想使用AspectJ,则可以使用TransactionTemplate。

@Service
public class MyService {
    @Autowired
    private TransactionTemplate transactionTemplate;

    private void process() {
        transactionTemplate.executeWithoutResult(status -> processInTransaction());
    }

    private void processInTransaction(){
        //...
    }
}

其他回答

与@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文档解释了这一点

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

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

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

是的,可以在私有方法上使用@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构建进行测试,作为完整性检查。