Краткое изложение исходного вопроса:Используя стандартные 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
, но он вам все равно нужен где-то , чтобы вы могли указывать на него с флагом -javaagent
JVM, как показано ниже:
-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 />
настроены в файле контекста приложения.