旧的“@Transactional来自同一类”的情况

原始问题的概要:使用带有AOP代理的标准Spring事务,不可能从同一类中的非@Transactional-marked方法调用@Transactional-marked方法并且在事务内(特别是由于前面提到的)代理)。 据说在AspectJ模式下使用Spring Transactions可以实现这一点,但它是如何完成的?

编辑:使用加载时编织在AspectJ模式下Spring事务的完整纲要:

将以下内容添加到META-INF/spring/applicationContext.xml

   

(我假设您已经在应用程序上下文中设置了AnnotationSessionFactoryBeanHibernateTransactionManager 。您可以将transaction-manager="transactionManager"作为属性添加到标记中,但是如果值为事务管理器bean的id属性实际上是“ transactionManager ”,然后它是多余的,因为“ transactionManager ”是该属性的默认值。)

添加META-INF/aop.xml 。 内容如下:

         

aspectjweaver-1.7.0.jarspring-aspects-3.1.2.RELEASE.jarclasspath 。 我使用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); } } 

这里的技巧是,如果TestClassTestMain一个字段,它的类将在加载应用程序上下文之前由ClassLoader加载。 由于编织是在类的加载时,并且这种编织是由Spring通过应用程序上下文完成的,所以它不会被编织,因为在加载应用程序上下文并知道它之前已经加载了类。

TestObjectTestDao的进一步细节并不重要。 假设他们使用JPA和Hibernate注释连接,并使用Hibernate进行持久化(因为他们是,他们这样做),并且在应用程序上下文文件中设置了所有必需的

编辑:使用编译时编织的AspectJ模式中Spring Transactions的完整纲要:

将以下内容添加到META-INF/spring/applicationContext.xml

  

(我假设您已经在应用程序上下文中设置了AnnotationSessionFactoryBeanHibernateTransactionManager 。您可以将transaction-manager="transactionManager"作为属性添加到标记中,但是如果值为事务管理器bean的id属性实际上是“ transactionManager ”,然后它是多余的,因为“ transactionManager ”是该属性的默认值。)

spring-aspects-3.1.2.RELEASE.jaraspectjrt-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.jarfull/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示例中一样: TestObjectTestDao的进一步细节并不重要。 假设他们使用JPA和Hibernate注释连接,并使用Hibernate进行持久化(因为他们是,他们这样做),并且在应用程序上下文文件中设置了所有必需的

通过阅读你的问题,我不清楚你被困在哪里,所以我将简要列出让AspectJ拦截你的@Transactional方法所需的内容。

  1. Spring配置文件中的
  2. 在您的Spring配置文件中, 也是如此。
  3. 位于META-INF文件夹中的aop.xml,直接位于类路径中。 这里也解释了这种格式。 它应包含处理@Transactional注释的方面定义:
  4. 同一个文件中的weaver元素也应该有一个include子句,告诉它编织哪些类:
  5. 类路径中spring-aop.jaraspectjweaver.jarspring-aspects.jarspring-aop.jar
  6. 使用标志-javaagent:/path/to/spring-instrument.jar (或spring-agent,在早期版本中调用)启动应用程序

最后一步可能没有必要。 这是一个非常简单的类,可以使用InstrumentationLoadTimeWeaver ,但如果不可用,Spring将尝试使用另一个加载时织入器。 不过,我从来没有尝试过。

现在,如果您认为已经完成了所有步骤并且仍然遇到问题,我可以建议在weaver上启用一些选项(在aop.xml中定义):

  

这使织布机输出了一堆正在编织的信息。 如果您看到正在编织的类,您可以TestClass那里查找您的TestClass 。 然后你至少有一个起点继续进行故障排除。


关于你的第二次编辑,“这几乎就像编织不会发生得足够快,无法在课程尝试执行之前编织。”,答案是肯定的 ,这可能发生。 我之前经历过这种情况 。

我对细节有点生疏,但基本上它是行中的东西,Spring将无法编织在创建应用程序上下文之前加载的类。 您是如何创建应用程序上下文的? 如果您以编程方式执行此操作,并且该类直接引用TestClass ,则可能会出现此问题,因为TestClass将过早加载。

不幸的是,我发现调试AspectJ是地狱。