Stary „@Transactional z tej samej klasy” Sytuacja
Streszczenie oryginalnego pytania: Używając standardowych Transakcji Spring przy użyciu proxy AOP, nie można wywołać metody @ Transakcyjnej-oznaczonej z metody innej niż Transakcyjna oznaczonej w tej samej klasie i być w ramach transakcji (szczególnie ze względu na wyżej wspomniane proxy). Jest to prawdopodobnie możliwe przy Spring Transactions w trybie AspectJ, ale jak to zrobić?
Edytować: Pełny przegląd transakcji Spring w trybie AspectJCzas ładowania Tkactwo:
Dodaj następujące elementy doMETA-INF/spring/applicationContext.xml
:
<tx:annotation-driven mode="aspectj" />
<context:load-time-weaver />
(Zakładam, że masz jużAnnotationSessionFactoryBean
i aHibernateTransactionManager
skonfigurowany w kontekście aplikacji. Możesz dodaćtransaction-manager="transactionManager"
jako atrybut do twojego<tx:annotation-driven />
tag, ale jeśli wartość menedżera fasoli transakcjiid
atrybut jest rzeczywiście ”transactionManager
„, to jest zbędne, jak”transactionManager
„jest wartością domyślną tego atrybutu.)
DodajMETA-INF/aop.xml
. Zawartość jest następująca:
<aspectj>
<aspects>
<aspect name="org.springframework.transaction.aspectj.AnnotationTransactionAspect" />
</aspects>
<weaver>
<include within="my.package..*" /><!--Whatever your package space is.-->
</weaver>
</aspectj>
Dodajaspectjweaver-1.7.0.jar
ispring-aspects-3.1.2.RELEASE.jar
dla Twojejclasspath
. Używam Maven jako mojego narzędzia do budowania, więc oto są<dependency />
deklaracje dla twojego projektuPOM.xml
plik:
<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
nie jest potrzebny jako<dependency />
na Twoimclasspath
, ale nadal tego potrzebujeszgdzieś abyś mógł na niego wskazać za pomocą-javaagent
Flaga JVM, jak następuje:
-javaagent:full\path\of\spring-instrument-3.1.2.RELEASE.jar
Pracuję w Eclipse Juno, więc aby to ustawić, poszedłem do okna -> Preferencje -> Java -> Zainstalowane środowiska JRE. Następnie kliknąłem zaznaczone JRE w polu listy i kliknąłem przycisk „Edytuj ...” po prawej stronie pola listy. Trzecie pole tekstowe w wyskakującym okienku jest oznaczone jako „Domyślne argumenty maszyny wirtualnej:”. To gdzie-javaagent
flaga powinna być wpisana lub skopiowana + wklejona.
Teraz moje aktualne klasy kodu testowego. Po pierwsze, moja główna klasa,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();
}
}
A potem moja klasa transakcyjna,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);
}
}
Sztuka polega na tym, że jeśliTestClass
to pole wTestMain
jego klasa zostanie załadowana przezClassLoader
przed załadowaniem kontekstu aplikacji. Ponieważ tkanie odbywa się w czasie ładowania klasy, a to tkanie jest wykonywane przez Spring za pomocą kontekstu aplikacji, nie zostanie ono utkane, ponieważ klasa jest już załadowana przed załadowaniem kontekstu aplikacji i jego świadomością.
Dalsze szczegółyTestObject
iTestDao
są nieważne. Załóżmy, że są one połączone z adnotacjami JPA i Hibernate i używają Hibernate do utrwalania (ponieważ są i robią), i że wszystkie wymagane<bean />
są ustawione w pliku kontekstowym aplikacji.
Edytować: Pełny przegląd transakcji Spring w trybie AspectJCzas kompilacji Tkactwo:
Dodaj następujące elementy doMETA-INF/spring/applicationContext.xml
:
<tx:annotation-driven mode="aspectj" />
(Zakładam, że masz jużAnnotationSessionFactoryBean
i aHibernateTransactionManager
skonfigurowany w kontekście aplikacji. Możesz dodaćtransaction-manager="transactionManager"
jako atrybut do twojego<tx:annotation-driven />
tag, ale jeśli wartość menedżera fasoli transakcjiid
atrybut jest rzeczywiście ”transactionManager
„, to jest zbędne, jak”transactionManager
„jest wartością domyślną tego atrybutu.)
Dodajspring-aspects-3.1.2.RELEASE.jar
iaspectjrt-1.7.0.jar
dla Twojejclasspath
. Używam Maven jako mojego narzędzia do budowania, więc oto jest<dependency />
deklaracje dlaPOM.xml
plik:
<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>
W Eclipse Juno: Pomoc -> Eclipse Marketplace -> pole tekstowe oznaczone „Znajdź:” -> wpisz „ajdt” -> naciśnij [Enter] -> „AspectJ Development Tools (Juno)” -> Zainstaluj -> Itd.
Po ponownym uruchomieniu Eclipse (zrobi cię) kliknij prawym przyciskiem myszy swój projekt, aby wyświetlić menu kontekstowe. Spójrz na dół: Konfiguruj -> Konwertuj na projekt AspectJ.
Dodaj następujące<plugin />
deklaracja w twoimPOM.xml
(znowu z 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>
Alternatywnie: Kliknij projekt prawym przyciskiem myszy, aby wyświetlić menu kontekstowe. Spójrz w dół: Narzędzia AspectJ -> Konfiguracja Ścieżka budowania AspectJ -> karta Ścieżka aspektu -> naciśnij „Dodaj zewnętrzne pliki JAR ...” -> zlokalizujfull/path/of/spring-aspects-3.1.2.RELEASE.jar
-> naciśnij „Otwórz” -> naciśnij „OK”.
Jeśli obrałeś trasę Maven,<plugin />
powyżej powinno być przerażające. Aby rozwiązać ten problem: Pomoc -> Zainstaluj nowe oprogramowanie ... -> naciśnij „Dodaj ...” -> wpisz, co chcesz w polu tekstowym „Nazwa:” -> wpisz lub skopiuj + wklejhttp://dist.springsource.org/release/AJDT/configurator/
w polu tekstowym oznaczonym „Lokalizacja:” -> naciśnij „OK” -> Poczekaj sekundę -> zaznacz pole wyboru obok „Integracja Maven dla Integracji Eclipse AJDT” -> naciśnij „Dalej>” -> Zainstaluj -> itd.
Po zainstalowaniu wtyczki i ponownym uruchomieniu Eclipse, błędy w twoimPOM.xml
plik powinien był zniknąć. Jeśli nie, kliknij prawym przyciskiem myszy swój projekt, aby wyświetlić menu kontekstowe: Maven -> Aktualizuj projekt -> naciśnij „OK”.
Teraz dla mojej rzeczywistej klasy kodu testowego. Tym razem tylko jedenTestClass.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);
}
}
Nie ma w tym żadnej sztuczki; ponieważ tkanie odbywa się w czasie kompilacji, czyli przed załadowaniem klasy i załadowaniem kontekstu aplikacji, kolejność tych dwóch rzeczy nie ma już znaczenia. Oznacza to, że wszystko może przejść do tej samej klasy. W Eclipse twój kod jest stale ponownie kompilowany za każdym razem, gdy klikniesz Save (kiedykolwiek zastanawiałeś się, co robi, gdy mówi „Buduj przestrzeń roboczą: (XX%)”?), Więc jest tkany i gotowy do pracy zawsze, gdy jesteś.
Podobnie jak w przykładzie Load-Time: dalsze szczegółyTestObject
iTestDao
są nieważne. Załóżmy, że są one połączone z adnotacjami JPA i Hibernate i używają Hibernate do utrwalania (ponieważ są i robią), i że wszystkie wymagane<bean />
są ustawione w pliku kontekstowym aplikacji.