Старая ситуация «@Transactional внутри того же класса»

Краткое изложение исходного вопроса:Используя стандартные Spring Transactions с AOP-проксированием, невозможно вызвать помеченный @Transactional -метод из не помеченного -@Transactional -метода в том же классе и находиться внутри транзакции (именно из-за вышеупомянутый прокси ). Предположительно это возможно с Spring Transactions в режиме AspectJ, но как это сделать?

Изменить:Полное изложение транзакций Spring в режиме AspectJ с использованием Загрузка -Время Плетение:

Добавьте следующее кMETA-INF/spring/applicationContext.xml:

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

<context:load-time-weaver />

(Я предполагаю, что у вас уже есть AnnotationSessionFactoryBeanи HibernateTransactionManager, настроенные в контексте приложения. Вы можете добавить transaction-manager="transactionManager"в качестве атрибута к вашему тегу <tx:annotation-driven />, но если значением атрибута idвашего bean-компонента менеджера транзакций на самом деле является "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не нужен как <dependency />на вашем classpath, но он вам все равно нужен где-то , чтобы вы могли указывать на него с флагом -javaagentJVM, как показано ниже:

-javaagent:full\path\of\spring-instrument-3.1.2.RELEASE.jar

Я работаю в Eclipse Juno, поэтому, чтобы установить это, я перешел в «Окно» -> «Настройки» -> «Java -» > «Установленные JRE». Затем я щелкнул отмеченную JRE в списке и нажал кнопку «Изменить...» справа от списка. Третье текстовое поле в появившемся всплывающем окне помечено как «Аргументы виртуальной машины по умолчанию :». Здесь следует ввести или скопировать+вставить флаг -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 />настроены в файле контекста приложения.

Изменить:Полное изложение транзакций Spring в режиме AspectJ с использованием Compile -Time Weaving:

Добавьте следующее вMETA-INF/spring/applicationContext.xml:

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

(Я предполагаю, что у вас уже есть AnnotationSessionFactoryBeanи HibernateTransactionManager, настроенные в контексте приложения. Вы можете добавить transaction-manager="transactionManager"в качестве атрибута к вашему тегу <tx:annotation-driven />, но если значением атрибута idвашего bean-компонента менеджера транзакций на самом деле является "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.

Добавьте следующее объявление <plugin />в свойPOM.xml(снова с Maven!):

<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 для Eclipse Интеграция с AJDT» -> нажмите «Далее >» -> Установить -> И т. д.

После установки плагина и перезапуска 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 ваш код постоянно перекомпилируется -каждый раз, когда вы нажимаете «Сохранить» (когда-либо задавались вопросом, что он делает, когда он говорит «Building workspace:(XX% )"? ), так что он сплетен и готов к работе, когда бы вы ни были.

Так же, как и в примере загрузки -времени :, дальнейшие детали TestObjectи TestDaoне важны. Предположим, что они связаны с аннотациями JPA и Hibernate и используют Hibernate для сохранения (, потому что они есть, и они делают ), и что все необходимые <bean />настроены в файле контекста приложения.

11
задан Random Human 6 August 2012 в 15:06
поделиться