La antigua situación "@Transactional desde dentro de la misma clase"

Sinopsis de la pregunta original: Al utilizar las transacciones Spring estándar con un proxy AOP, no es posible llamar a un método @ con marca transaccional desde un método con marca no transaccional @ en la misma clase y estar dentro de una transacción (específicamente debido al proxy antes mencionado). Esto es supuestamente posible con Spring Transactions en modo AspectJ, pero ¿cómo se hace?

Editar: El resumen completo de Spring Transactions en modo AspectJ usandoTiempo de carga Tejeduría:

Agregue lo siguiente aMETA-INF/spring/applicationContext.xml:

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

<context:load-time-weaver />

(Asumiré que ya tienes unaAnnotationSessionFactoryBean y unHibernateTransactionManager configurar en el contexto de la aplicación. Puedes añadirtransaction-manager="transactionManager" como un atributo para su<tx:annotation-driven /> etiqueta, pero si el valor de su administrador de transacciones de frijolid atributo es en realidad "transactionManager", entonces es redundante, como"transactionManager"es el valor por defecto de ese atributo.)

AñadirMETA-INF/aop.xml. Los contenidos son los siguientes:

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

Añadiraspectjweaver-1.7.0.jar yspring-aspects-3.1.2.RELEASE.jar para ustedclasspath. Yo uso Maven como mi herramienta de compilación, así que aquí están las<dependency /> Declaraciones para tu proyecto.POM.xml expediente:

<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 no es necesario como un<dependency /> en tuclasspath, pero todavia lo necesitasalgun lado para que puedas señalarlo con el-javaagent Bandera JVM, de la siguiente manera:

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

Estoy trabajando en Eclipse Juno, así que para configurar esto fui a Ventana -> Preferencias -> Java -> JRE instalados. Luego hice clic en el JRE marcado en el cuadro de lista y hice clic en el botón "Editar ..." a la derecha del cuadro de lista. El tercer cuadro de texto en la ventana emergente resultante está etiquetado como "Argumentos de VM predeterminados:". Aquí es donde el-javaagent la bandera se debe escribir o copiar y pegar.

Ahora para mis clases de código de prueba real. Primero, mi clase principal,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();
  }
}

Y luego mi clase transaccional,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);
  }
}

El truco aquí es que si elTestClass es un campo enTestMain su clase será cargada por elClassLoader antes de que se cargue el contexto de la aplicación. Dado que el tejido está en el tiempo de carga de la clase, y este tejido lo realiza Spring a través del contexto de la aplicación, no se tejerá porque la clase ya está cargada antes de que el contexto de la aplicación se cargue y sea consciente de ello.

Los detalles adicionales deTestObject yTestDao no son importantes Suponga que están conectados con las anotaciones JPA e Hibernate y use Hibernate para la persistencia (porque lo están, y lo hacen), y que todo lo necesario<bean />'s se configuran en el archivo de contexto de la aplicación.

Editar: El resumen completo de Spring Transactions en modo AspectJ usandoTiempo de compilación Tejeduría:

Agregue lo siguiente aMETA-INF/spring/applicationContext.xml:

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

(Asumiré que ya tienes unaAnnotationSessionFactoryBean y unHibernateTransactionManager configurar en el contexto de la aplicación. Puedes añadirtransaction-manager="transactionManager" como un atributo para su<tx:annotation-driven /> etiqueta, pero si el valor de su administrador de transacciones de frijolid atributo es en realidad "transactionManager", entonces es redundante, como"transactionManager"es el valor por defecto de ese atributo.)

Añadirspring-aspects-3.1.2.RELEASE.jar yaspectjrt-1.7.0.jar para ustedclasspath. Yo uso Maven como mi herramienta de compilación, así que aquí está el<dependency /> declaraciones para elPOM.xml expediente:

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

En Eclipse Juno: Ayuda -> Eclipse Marketplace -> cuadro de texto etiquetado "Buscar:" -> escriba "ajdt" -> presione [Entrar] -> "AspectJ Development Tools (Juno)" -> Instalar -> Etc.

Después de reiniciar Eclipse (te hará), haz clic derecho en tu proyecto para abrir el menú contextual. Mire cerca de la parte inferior: Configurar -> Convertir a AspectJ Project.

Agrega lo siguiente<plugin /> declaración en tuPOM.xml (De nuevo con el 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>

Alternativa: haga clic derecho en su proyecto para abrir el menú contextual. Mire cerca de la parte inferior: Herramientas AspectJ -> Configurar ruta de compilación AspectJ -> pestaña Ruta de aspecto -> presione "Agregar JAR externos ..." -> localice lafull/path/of/spring-aspects-3.1.2.RELEASE.jar -> presione "Abrir" -> presione "OK".

Si tomaste la ruta Maven, la<plugin /> Lo anterior debería estar enloqueciendo. Para solucionar esto: Ayuda -> Instalar nuevo software ... -> presione "Agregar ..." -> escriba lo que quiera en el cuadro de texto llamado "Nombre:" -> escriba o copie + peguehttp://dist.springsource.org/release/AJDT/configurator/ en el cuadro de texto etiquetado "Ubicación:" -> presione "Aceptar" -> Espere un segundo -> marque la casilla de verificación principal junto a "Integración de Maven para la integración de Eclipse AJDT" -> presione "Siguiente>" -> Instalar -> Etc.

Cuando el complemento está instalado, y ha reiniciado Eclipse, los errores en suPOM.xml archivo debería haber desaparecido. De lo contrario, haga clic con el botón derecho en el proyecto para abrir el menú contextual: Maven -> Actualizar proyecto -> presione "OK".

Ahora para mi clase de código de prueba real. Sólo uno esta vez,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);
  }
}

No hay truco para este; dado que el tejido se produce en tiempo de compilación, que es antes de que se carguen tanto la carga de clases como el contexto de la aplicación, el orden de estas dos cosas ya no importa. Esto significa que todo puede ir en la misma clase. En Eclipse, su código se vuelve a compilar constantemente cada vez que presiona Guardar (alguna vez se preguntó qué estaba haciendo mientras decía "¿Espacio de trabajo en la construcción: (XX%)"?), Así que está entretejido y listo para funcionar cuando usted lo esté.

Al igual que en el ejemplo de tiempo de carga: los detalles adicionales deTestObject yTestDao no son importantes Suponga que están conectados con las anotaciones JPA e Hibernate y use Hibernate para la persistencia (porque lo están, y lo hacen), y que todo lo necesario<bean />'s se configuran en el archivo de contexto de la aplicación.

Respuestas a la pregunta(1)

Su respuesta a la pregunta