Die alte Situation "@Transactional aus derselben Klasse"

Inhaltsangabe der ursprünglichen Frage: Bei Verwendung von Standard-Spring-Transaktionen mit AOP-Proxy ist es nicht möglich, eine mit @ Transactional markierte Methode von einer nicht mit @ Transactional markierten Methode in derselben Klasse aufzurufen und sich innerhalb einer Transaktion zu befinden (insbesondere aufgrund des oben genannten Proxys). Dies ist angeblich mit Spring Transactions im AspectJ-Modus möglich, aber wie wird das gemacht?

Bearbeiten: Die vollständige Übersicht für Frühlingstransaktionen im AspectJ-Modus mitLadezeit Weberei:

Fügen Sie Folgendes hinzu zuMETA-INF/spring/applicationContext.xml:

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

<context:load-time-weaver />

Ich gehe davon aus, dass Sie bereits eine habenAnnotationSessionFactoryBean und einHibernateTransactionManager im Anwendungskontext eingerichtet. Du kannst hinzufügentransaction-manager="transactionManager" als ein Attribut zu Ihrem<tx:annotation-driven /> tag, aber wenn der Wert Ihres Transaktionsmanagers Bean istid Attribut ist eigentlich "transactionManager", dann ist es überflüssig, als"transactionManager"ist der Standardwert dieses Attributs.)

HinzufügenMETA-INF/aop.xml. Inhalte sind wie folgt:

<aspectj>
  <aspects>
    <aspect name="org.springframework.transaction.aspectj.AnnotationTransactionAspect" />
  </aspects>
  <weaver>
    <include within="my.package..*" /><!--Whatever your package space is.-->
  </weaver>
</aspectj>

Hinzufügenaspectjweaver-1.7.0.jar undspring-aspects-3.1.2.RELEASE.jar zu deinemclasspath. Ich benutze Maven als mein Build-Tool, also hier sind die<dependency /> Erklärungen für Ihr ProjektPOM.xml Datei:

<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 wird nicht benötigt als<dependency /> auf Ihremclasspath, aber du brauchst es immer nochirgendwo so dass Sie mit dem Mauszeiger darauf zeigen können-javaagent JVM-Flag wie folgt:

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

Ich arbeite in Eclipse Juno. Um dies einzustellen, ging ich zu Fenster -> Einstellungen -> Java -> Installierte JREs. Dann habe ich auf die markierte JRE im Listenfeld geklickt und auf die Schaltfläche "Bearbeiten ..." rechts neben dem Listenfeld geklickt. Das dritte Textfeld im daraufhin angezeigten Popup-Fenster trägt die Bezeichnung "Standard-VM-Argumente:". Dies ist, wo die-javaagent flag sollte eingegeben oder kopiert + eingefügt werden.

Nun zu meinen eigentlichen Testcode-Klassen. Erstens, meine Hauptklasse,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();
  }
}

Und dann meine Transaktionsklasse,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);
  }
}

Der Trick hier ist, dass, wenn dieTestClass ist ein Feld inTestMain seine Klasse wird von der geladenClassLoader bevor der Anwendungskontext geladen wird. Da das Weben zum Ladezeitpunkt der Klasse stattfindet und dieses Weben von Spring über den Anwendungskontext erfolgt, wird es nicht gewebt, da die Klasse bereits geladen ist, bevor der Anwendungskontext geladen wurde und dies bemerkt.

Die weiteren Einzelheiten vonTestObject undTestDao sind unwichtig. Angenommen, sie sind mit JPA- und Hibernate-Annotationen verbunden und verwenden Hibernate für die Persistenz (weil sie es sind und tun) und das alles, was erforderlich ist<bean />werden in der Anwendungskontextdatei eingerichtet.

Bearbeiten: Die vollständige Übersicht für Frühlingstransaktionen im AspectJ-Modus mitKompilierungszeit Weberei:

Fügen Sie Folgendes hinzu zuMETA-INF/spring/applicationContext.xml:

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

Ich gehe davon aus, dass Sie bereits eine habenAnnotationSessionFactoryBean und einHibernateTransactionManager im Anwendungskontext eingerichtet. Du kannst hinzufügentransaction-manager="transactionManager" als ein Attribut zu Ihrem<tx:annotation-driven /> tag, aber wenn der Wert Ihres Transaktionsmanagers Bean istid Attribut ist eigentlich "transactionManager", dann ist es überflüssig, als"transactionManager"ist der Standardwert dieses Attributs.)

Hinzufügenspring-aspects-3.1.2.RELEASE.jar undaspectjrt-1.7.0.jar zu deinemclasspath. Ich benutze Maven als mein Build-Tool<dependency /> Erklärungen für diePOM.xml Datei:

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

In Eclipse Juno: Hilfe -> Eclipse Marketplace -> Textfeld "Suchen:" -> "ajdt" eingeben -> [Eingabetaste] drücken -> "AspectJ Development Tools (Juno)" -> Installieren -> usw.

Klicken Sie nach dem Neustart von Eclipse mit der rechten Maustaste auf Ihr Projekt, um das Kontextmenü aufzurufen. Schauen Sie ganz unten nach: Konfigurieren -> In AspectJ-Projekt konvertieren.

Fügen Sie Folgendes hinzu<plugin /> Erklärung in IhremPOM.xml (Nochmal mit dem 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>

Alternative: Klicken Sie mit der rechten Maustaste auf Ihr Projekt, um das Kontextmenü aufzurufen. Schauen Sie in der Nähe der Unterseite nach: AspectJ Tools -> AspectJ Build Path konfigurieren -> Registerkarte Aspect Path -> drücken Sie auf "Add External JARs ..." -> suchen Sie diefull/path/of/spring-aspects-3.1.2.RELEASE.jar -> "Öffnen" drücken -> "OK" drücken.

Wenn Sie die Maven - Route gewählt haben, hat die<plugin /> oben sollte ausflippen. So beheben Sie das Problem: Hilfe -> Neue Software installieren ... -> drücken Sie "Hinzufügen ..." -> geben Sie in das Textfeld "Name:" ein, was Sie möchten -> geben Sie ein oder kopieren und einfügenhttp://dist.springsource.org/release/AJDT/configurator/ im Textfeld mit der Bezeichnung "Ort:" -> drücken Sie "OK" -> Warten Sie eine Sekunde -> aktivieren Sie das übergeordnete Kontrollkästchen neben "Maven Integration für Eclipse AJDT-Integration" -> drücken Sie "Weiter>" -> Installieren -> usw.

Wenn das Plugin installiert ist und Sie Eclipse neu gestartet haben, werden die Fehler in IhremPOM.xml Datei hätte verschwinden sollen. Wenn nicht, klicken Sie mit der rechten Maustaste auf Ihr Projekt, um das Kontextmenü aufzurufen: Maven -> Projekt aktualisieren -> drücken Sie "OK".

Nun zu meiner eigentlichen Testcode-Klasse. Nur eines dieses Mal,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);
  }
}

Es gibt keinen Trick für diesen einen; Da das Weben zur Kompilierungszeit stattfindet, also vor dem Laden der Klasse und vor dem Laden des Anwendungskontexts, spielt die Reihenfolge dieser beiden Dinge keine Rolle mehr. Dies bedeutet, dass alles in der gleichen Klasse gehen kann. In Eclipse wird der Code jedes Mal neu kompiliert, wenn Sie auf "Speichern" klicken (Sie haben sich schon einmal gefragt, was er getan hat, während "Arbeitsbereich erstellen: (XX%)?" Angezeigt wird), sodass er gewebt und jederzeit einsatzbereit ist.

Genau wie im Load-Time-Beispiel: die weiteren Details vonTestObject undTestDao sind unwichtig. Angenommen, sie sind mit JPA- und Hibernate-Annotationen verbunden und verwenden Hibernate für die Persistenz (weil sie es sind und tun) und das alles, was erforderlich ist<bean />werden in der Anwendungskontextdatei eingerichtet.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage