旧的“@Transactional来自同一类”的情况
原始问题的概要:使用带有AOP代理的标准Spring事务,不可能从同一类中的非@Transactional-marked方法调用@Transactional-marked方法并且在事务内(特别是由于前面提到的)代理)。 据说在AspectJ模式下使用Spring Transactions可以实现这一点,但它是如何完成的?
编辑:使用加载时编织在AspectJ模式下Spring事务的完整纲要:
将以下内容添加到META-INF/spring/applicationContext.xml
:
(我假设您已经在应用程序上下文中设置了AnnotationSessionFactoryBean
和HibernateTransactionManager
。您可以将transaction-manager="transactionManager"
作为属性添加到标记中,但是如果值为事务管理器bean的
id
属性实际上是“ transactionManager
”,然后它是多余的,因为“ transactionManager
”是该属性的默认值。)
添加META-INF/aop.xml
。 内容如下:
将aspectjweaver-1.7.0.jar
和spring-aspects-3.1.2.RELEASE.jar
到classpath
。 我使用Maven作为我的构建工具,所以这里是项目的POM.xml
文件的声明:
org.aspectj aspectjweaver 1.7.0 org.springframework spring-aspects 3.1.2.RELEASE
spring-instrument-3.1.2.RELEASE.jar
不需要作为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进行持久化(因为他们是,他们这样做),并且在应用程序上下文文件中设置了所有必需的 。
编辑:使用编译时编织的AspectJ模式中Spring Transactions的完整纲要:
将以下内容添加到META-INF/spring/applicationContext.xml
:
(我假设您已经在应用程序上下文中设置了AnnotationSessionFactoryBean
和HibernateTransactionManager
。您可以将transaction-manager="transactionManager"
作为属性添加到标记中,但是如果值为事务管理器bean的
id
属性实际上是“ transactionManager
”,然后它是多余的,因为“ transactionManager
”是该属性的默认值。)
将spring-aspects-3.1.2.RELEASE.jar
和aspectjrt-1.7.0.jar
spring-aspects-3.1.2.RELEASE.jar
添加到classpath
。 我使用Maven作为我的构建工具,所以这里是POM.xml
文件的声明:
org.springframework spring-aspects 3.1.2.RELEASE org.aspectj aspectjrt 1.7.0
在Eclipse Juno中:帮助 – > Eclipse Marketplace – >文本框标记为“查找:” – >键入“ajdt” – >单击[Enter] – >“AspectJ开发工具(Juno)” – >安装 – >等等。
重新启动Eclipse(它将使您)后,右键单击您的项目以显示上下文菜单。 看看底部附近:配置 – >转换为AspectJ项目。
在POM.xml
添加以下声明(再次使用Maven!):
org.codehaus.mojo aspectj-maven-plugin 1.4 org.springframework spring-aspects compile test-compile
替代方案:右键单击项目以显示上下文菜单。 看看底部附近:AspectJ工具 – >配置AspectJ构建路径 – > Aspect Path选项卡 – >按“Add External JARs …” – >找到full/path/of/spring-aspects-3.1.2.RELEASE.jar
的full/path/of/spring-aspects-3.1.2.RELEASE.jar
– >按“打开” – >按“确定”。
如果你采用Maven路线,上面的应该是吓坏了。 要解决此问题:帮助 – >安装新软件… – >按“添加…” – >在标有“名称:”的文本框中键入您喜欢的任何内容 – >键入或复制+粘贴
http://dist.springsource.org/release/AJDT/configurator/
在标有“Location:”的文本框中 – >按“OK” – >等一下 – >选中“Maven Integration for Eclipse AJDT Integration”旁边的父复选框 – >按“下一步>“ – >安装 – >等。
安装插件并重新启动Eclipse后, POM.xml
文件中的错误应该已经消失。 如果没有,右键单击您的项目以显示上下文菜单:Maven – > Update Project – >按“OK”。
现在我的实际测试代码类。 这次只有一个, 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); } }
这个没有诀窍; 由于编织发生在编译时,即在类加载和应用程序上下文加载之前,这两个事物的顺序不再重要。 这意味着一切都可以在同一个class级。 在Eclipse中,每次点击Save时你的代码都会不断被重新编译(曾经想过它在做什么时会说“建立工作空间:(XX%)”?),所以无论何时你都可以编织它。
就像在Load-Time示例中一样: TestObject
和TestDao
的进一步细节并不重要。 假设他们使用JPA和Hibernate注释连接,并使用Hibernate进行持久化(因为他们是,他们这样做),并且在应用程序上下文文件中设置了所有必需的 。
通过阅读你的问题,我不清楚你被困在哪里,所以我将简要列出让AspectJ拦截你的@Transactional
方法所需的内容。
- Spring配置文件中的
。 - 在您的Spring配置文件中,
也是如此。 - 位于META-INF文件夹中的aop.xml,直接位于类路径中。 这里也解释了这种格式。 它应包含处理
@Transactional
注释的方面定义: - 同一个文件中的weaver元素也应该有一个include子句,告诉它编织哪些类:
- 类路径中
spring-aop.jar
,aspectjweaver.jar
,spring-aspects.jar
和spring-aop.jar
- 使用标志
-javaagent:/path/to/spring-instrument.jar
(或spring-agent,在早期版本中调用)启动应用程序
最后一步可能没有必要。 这是一个非常简单的类,可以使用InstrumentationLoadTimeWeaver
,但如果不可用,Spring将尝试使用另一个加载时织入器。 不过,我从来没有尝试过。
现在,如果您认为已经完成了所有步骤并且仍然遇到问题,我可以建议在weaver上启用一些选项(在aop.xml中定义):
这使织布机输出了一堆正在编织的信息。 如果您看到正在编织的类,您可以TestClass
那里查找您的TestClass
。 然后你至少有一个起点继续进行故障排除。
关于你的第二次编辑,“这几乎就像编织不会发生得足够快,无法在课程尝试执行之前编织。”,答案是肯定的 ,这可能发生。 我之前经历过这种情况 。
我对细节有点生疏,但基本上它是行中的东西,Spring将无法编织在创建应用程序上下文之前加载的类。 您是如何创建应用程序上下文的? 如果您以编程方式执行此操作,并且该类直接引用TestClass
,则可能会出现此问题,因为TestClass
将过早加载。
不幸的是,我发现调试AspectJ是地狱。