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

时间:2012-07-23 15:33:45

标签: java spring-aop spring-transactions aop

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

编辑:使用加载时间编织:

在AspectJ模式下进行Spring Transactions的完整纲要

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

<tx:annotation-driven mode="aspectj" />

<context:load-time-weaver />

(我假设您已在应用程序上下文中设置了AnnotationSessionFactoryBeanHibernateTransactionManager。您可以将transaction-manager="transactionManager"作为属性添加到{{1} }标记,但如果事务管理器bean的<tx:annotation-driven />属性的值实际上是&#34; id&#34;,则它是多余的,因为&#34 ; transactionManager&#34;是该属性的默认值。)

添加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。我使用Maven作为构建工具,因此以下是项目classpath文件的<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不需要

<dependency />,但您仍然需要某处,以便您可以使用{{}来指向它1}} JVM标志,如下所示:

classpath

我在Eclipse Juno工作,所以为了设置这个我去了Window - &gt;偏好 - &gt; Java - &gt;已安装的JRE。然后我在列表框中单击选中的JRE,然后单击&#34;编辑...&#34;列表框右侧的按钮。生成的弹出窗口中的第三个文本框标有&#34;默认VM参数:&#34;。这是应该键入-javaagent标志或复制+粘贴的地方。

现在我的实际测试代码类。首先,我的主要课程-javaagent:full\path\of\spring-instrument-3.1.2.RELEASE.jar

-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.javapackage 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将加载其类。由于编织是在类的加载时,并且这种编织是由Spring通过应用程序上下文完成的,所以它不会被编织,因为在加载应用程序上下文并知道它之前已经加载了类。

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

编辑:使用编译时间编织:

在AspectJ模式下进行Spring Transactions的完整概述

将以下内容添加到TestDao

<bean />

(我假设您已在应用程序上下文中设置了META-INF/spring/applicationContext.xml<tx:annotation-driven mode="aspectj" /> 。您可以将AnnotationSessionFactoryBean作为属性添加到{{1} }标记,但如果事务管理器bean的HibernateTransactionManager属性的值实际上是&#34; transaction-manager="transactionManager"&#34;,则它是多余的,因为&#34 ; <tx:annotation-driven />&#34;是该属性的默认值。)

idtransactionManager添加到transactionManager。我使用Maven作为构建工具,所以这里是spring-aspects-3.1.2.RELEASE.jar文件的aspectjrt-1.7.0.jar声明:

classpath

在Eclipse Juno中:帮助 - &gt; Eclipse Marketplace - &gt;文本框标有&#34;查找:&#34; - &GT;输入&#34; ajdt&#34; - &GT;点击[Enter] - &gt; &#34; AspectJ开发工具(Juno)&#34; - &GT;安装 - &gt;等

重启Eclipse后(它会让你),右键单击你的项目以显示上下文菜单。看看底部附近:配置 - &gt;转换为AspectJ项目。

<dependency />中添加以下POM.xml声明(再次使用Maven!):

<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>

替代方案:右键单击项目以显示上下文菜单。看看底部附近:AspectJ工具 - &gt;配置AspectJ构建路径 - &gt; Aspect Path选项卡 - &gt;按&#34;添加外部JAR ...&#34; - &GT;找到<plugin /> - &gt;按&#34;打开&#34; - &GT;按&#34; OK&#34;。

如果您采用Maven路线,上面的POM.xml应该是吓坏了。解决此问题:帮助 - &gt;安装新软件... - &gt;按&#34;添加...&#34; - &GT;在标有&#34的文本框中输入您喜欢的内容;名称:&#34; - &GT;在标有&#34;位置:&#34;的文本框中输入或复制+粘贴<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> - &GT;按&#34; OK&#34; - &GT;等一下 - &gt;检查&#34; Maven Integration for Eclipse AJDT Integration&#34;旁边的父复选框。 - &GT;按&#34;下一步&gt;&#34; - &GT;安装 - &gt;等

安装插件后,您重新启动了Eclipse,full/path/of/spring-aspects-3.1.2.RELEASE.jar文件中的错误应该已经消失。如果没有,请右键单击您的项目以显示上下文菜单:Maven - &gt;更新项目 - &gt;按&#34; OK&#34;。

现在我的实际测试代码类。这次只有一次,<plugin />

http://dist.springsource.org/release/AJDT/configurator/

这个没有诀窍;由于编织发生在编译时,即在类加载和应用程序上下文加载之前,这两个事物的顺序不再重要。这意味着一切都可以进入同一个班级。在Eclipse中,每次点击Save时,你的代码都会不断被重新编译(曾经想知道它在做什么,而它说'#34;建立工作区:(XX%)&#34;?),所以它随时随地编织并准备好。

就像在Load-Time示例中一样:POM.xmlTestClass.java的更多细节并不重要。假设他们使用JPA和Hibernate注释连接,并使用Hibernate进行持久化(因为他们是,他们这样做),并且在应用程序上下文文件中设置了所有必需的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); } } 。 / p>

1 个答案:

答案 0 :(得分:9)

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

    Spring配置文件中的
  1. <tx:annotation-driven mode="aspectj"/>
  2. <context:load-time-weaver/>以及Spring配置文件。
  3. 直接位于类路径中的META-INF文件夹中的aop.xml。其格式也解释为here。它应包含处理@Transactional注释的方面定义:<aspect name="org.springframework.transaction.aspectj.AnnotationTransactionAspect"/>
  4. 同一个文件中的weaver元素还应该有一个include子句,告诉它编织哪些类:<include within="foo.*"/>
  5. 类路径中的
  6. aspectjrt.jaraspectjweaver.jarspring-aspects.jarspring-aop.jar
  7. 使用标志-javaagent:/path/to/spring-instrument.jar(或弹簧代理,在早期版本中调用)启动应用程序
  8. 最后一步可能没有必要。这是一个非常简单的类,可以使用InstrumentationLoadTimeWeaver,但如果不可用,Spring将尝试使用另一个加载时间weaver。不过,我从未尝试过。

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

    <weaver options="-XnoInline -Xreweavable -verbose -debug -showWeaveInfo">
    

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


    关于你的第二次编辑,“这几乎就像编织在组课程尝试执行之前编织的速度不够快。”,答案是,这可能发生。 I experienced a situation like this before

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

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