原始问题的概要:使用带有AOP代理的标准Spring Transactions,不可能从同一类中的非@Transactional-marked方法调用@Transactional-marked方法并且在事务中(特别是由于前面提到的代理人)。这在 AspectJ 模式下的 Spring Transactions 中是可能的,但它是如何完成的呢?
编辑:在 AspectJ 模式下使用Load-Time Weaving的 Spring Transactions 的完整纲要:
将以下内容添加到META-INF/spring/applicationContext.xml
:
<tx:annotation-driven mode="aspectj" />
<context:load-time-weaver />
(我假设您已经在应用程序上下文中设置了一个AnnotationSessionFactoryBean
和是该属性的默认值。)HibernateTransactionManager
transaction-manager="transactionManager"
<tx:annotation-driven />
id
transactionManager
transactionManager
添加META-INF/aop.xml
. 内容如下:
<aspectj>
<aspects>
<aspect name="org.springframework.transaction.aspectj.AnnotationTransactionAspect" />
</aspects>
<weaver>
<include within="my.package..*" /><!--Whatever your package space is.-->
</weaver>
</aspectj>
aspectjweaver-1.7.0.jar
将和添加spring-aspects-3.1.2.RELEASE.jar
到您的classpath
. 我使用 Maven 作为我的构建工具,所以这里是<dependency />
你项目POM.xml
文件的声明:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
spring-instrument-3.1.2.RELEASE.jar
不需要作为 a<dependency />
在您的 上classpath
,但您仍然需要它,以便您可以使用-javaagent
JVM 标志指向它,如下所示:
-javaagent:full\path\of\spring-instrument-3.1.2.RELEASE.jar
我在 Eclipse Juno 中工作,所以要设置它,我转到 Window -> Preferences -> Java -> Installed JREs。然后我点击列表框中选中的JRE,然后点击列表框右侧的“编辑...”按钮。结果弹出窗口中的第三个文本框标记为“默认 VM 参数:”。这是-javaagent
应该输入或复制+粘贴标志的地方。
现在是我的实际测试代码类。首先,我的主要课程TestMain.java
:
package my.package;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMain {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml");
TestClass testClass = applicationContext.getBean(TestClass.class);
testClass.nonTransactionalMethod();
}
}
然后是我的事务类TestClass.java
:
package my.package;
import my.package.TestDao;
import my.package.TestObject;
import org.springframework.transaction.annotation.Transactional;
public void TestClass {
private TestDao testDao;
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
public TestDao getTestDao() {
return testDao;
}
public void nonTransactionalMethod() {
transactionalMethod();
}
@Transactional
private void transactionalMethod() {
TestObject testObject = new TestObject();
testObject.setId(1L);
testDao.save(testObject);
}
}
这里的技巧是,如果它TestClass
是其类中的一个字段,TestMain
则将在加载ClassLoader
应用程序上下文之前加载。由于编织是在类的加载时进行的,并且这种编织是由 Spring 通过应用程序上下文完成的,所以它不会被编织,因为在加载应用程序上下文并知道它之前,该类已经加载。
TestObject
和的进一步细节TestDao
并不重要。假设它们与 JPA 和 Hibernate 注释连接起来,并使用 Hibernate 进行持久性(因为它们是,而且它们确实如此),并且所有必需<bean />
的 's 都在应用程序上下文文件中设置。
编辑:在 AspectJ 模式下使用编译时编织的 Spring Transactions 的完整纲要:
将以下内容添加到META-INF/spring/applicationContext.xml
:
<tx:annotation-driven mode="aspectj" />
(我假设您已经在应用程序上下文中设置了一个AnnotationSessionFactoryBean
和是该属性的默认值。)HibernateTransactionManager
transaction-manager="transactionManager"
<tx:annotation-driven />
id
transactionManager
transactionManager
spring-aspects-3.1.2.RELEASE.jar
将和添加aspectjrt-1.7.0.jar
到您的classpath
. 我使用 Maven 作为我的构建工具,所以这里是文件的<dependency />
声明POM.xml
:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.0</version>
</dependency>
在 Eclipse Juno 中:帮助 -> Eclipse Marketplace -> 标有“查找:”的文本框 -> 键入“ajdt” -> 点击 [Enter] -> “AspectJ 开发工具 (Juno)” -> 安装 -> 等等。
重新启动 Eclipse(它会让你)后,右键单击你的项目以调出上下文菜单。查看底部附近:配置 -> 转换为 AspectJ 项目。
在您的(再次使用 Maven!)中添加以下<plugin />
声明:POM.xml
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
替代方法:右键单击您的项目以调出上下文菜单。查看底部附近:AspectJ 工具 -> 配置 AspectJ 构建路径 -> 方面路径选项卡 -> 按“添加外部 JAR...” -> 找到full/path/of/spring-aspects-3.1.2.RELEASE.jar
-> 按“打开” -> 按“确定”。
如果你走 Maven 路线,<plugin />
上面的内容应该会吓坏了。要解决此问题:帮助 -> 安装新软件... -> 按“添加...” -> 在标有“名称:”的文本框中键入您喜欢的任何内容 -> 在标有“的文本框中键入或复制+http://dist.springsource.org/release/AJDT/configurator/
粘贴位置:”-> 按“确定”-> 稍等-> 选中“Maven Integration for Eclipse AJDT Integration”旁边的父复选框-> 按“下一步>”-> 安装-> 等等。
安装插件并重新启动 Eclipse 后,POM.xml
文件中的错误应该已经消失。如果没有,请右键单击您的项目以调出上下文菜单:Maven -> 更新项目 -> 按“确定”。
现在是我的实际测试代码类。这次只有一个,TestClass.java
:
package my.package;
import my.package.TestDao;
import my.package.TestObject;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.annotation.Transactional;
public void TestClass {
private TestDao testDao;
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
public TestDao getTestDao() {
return testDao;
}
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml");
TestClass testClass = applicationContext.getBean(TestClass.class);
testClass.nonTransactionalMethod();
}
public void nonTransactionalMethod() {
transactionalMethod();
}
@Transactional
private void transactionalMethod() {
TestObject testObject = new TestObject();
testObject.setId(1L);
testDao.save(testObject);
}
}
这个没有技巧。由于编织发生在编译时,即在类加载和应用程序上下文加载之前,这两件事的顺序不再重要。这意味着所有东西都可以放在同一个类中。在 Eclipse 中,每次您点击 Save 时,您的代码都会不断地重新编译(有没有想过它在说“Building workspace: (XX%)”时它在做什么?),所以它已经被编织并随时可以使用。
就像在加载时间示例中一样: 和 的更多细节TestObject
并不TestDao
重要。假设它们与 JPA 和 Hibernate 注释连接起来,并使用 Hibernate 进行持久性(因为它们是,而且它们确实如此),并且所有必需<bean />
的 's 都在应用程序上下文文件中设置。