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.

questionAnswers(1)

yourAnswerToTheQuestion