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

当前回答

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

其他回答

问题不是私有的或公共的,问题是:如何调用它以及您使用哪个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参考:使用@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构建进行测试,作为完整性检查。

默认情况下,@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