2010-12-09 29 views

Respuesta

172

La respuesta a su pregunta es no - @Transactional tendrá ningún efecto si se utiliza para anotar métodos privados. El generador de proxy los ignorará.

Esto está documentado en Spring Manual chapter 10.5.6:

visibilidad Método y @Transactional

Al utilizar servidores proxy, debe aplicar el @Transactional anotación única a métodos con visibilidad pública. Si hacer anotaciones protegidos, privada o métodos del paquete visible con la @Transactional anotación, no se produce ningún error , pero el método anotado no presente los ajustes transaccionales configurados. Considere el uso de de AspectJ (consulte a continuación) si necesita para anotar métodos no públicos.

+0

¿Estás seguro de esto? No esperaría que haga la diferencia. – willcodejavaforfood

+27

Aunque funcionará con mode = "aspectj". – waxwing

+1

@willcodejavaforfood: Ver la cita – skaffman

3

La respuesta es no. Por favor ver Spring Reference: Using @Transactional :

El @Transactional anotación pueden ser colocados antes de una definición de interfaz, un método en una interfaz, una definición de clase, o un método públicoen una clase

23

Por defecto, el El atributo @Transactional funciona solo cuando se llama a un método anotado en una referencia obtenida de applicationContext.

public class Bean { 
    public void doStuff() { 
    doTransactionStuff(); 
    } 
    @Transactional 
    public void doTransactionStuff() { 

    } 
} 

Esto se abrirá una transacción:

Bean bean = (Bean)appContext.getBean("bean"); 
bean.doTransactionStuff(); 

Este no:

Bean bean = (Bean)appContext.getBean("bean"); 
bean.doStuff(); 

Spring Reference: Using @Transactional

Nota: En el modo proxy (que es el valor por defecto), solo las llamadas a métodos 'externos' que entren a través del proxy ser interceptado Esto significa que la "auto invocación", es decir, un método dentro del objeto objetivo que llama a otro método del objeto objetivo, no dará lugar a una transacción real en el tiempo de ejecución, incluso si el método invocado está marcado con @Transactional.

Considere el uso del modo AspectJ (consulte a continuación) si espera que las autovocaciones se envuelvan con transacciones también. En este caso, no habrá un proxy en primer lugar; en su lugar, la clase objetivo será 'tejida' (es decir, se modificará su código de bytes) para convertir @Transactional en el comportamiento del tiempo de ejecución en cualquier tipo de método.

+0

¿Quiere decir bean = new Bean() ;? – willcodejavaforfood

+0

No. Si creo beans con new Bean(), la anotación nunca funcionará al menos sin usar Aspect-J. –

+1

gracias! Esto explica el comportamiento extraño que estaba observando. Bastante contra intuitivo esta restricción de invocación de método interno ... –

3

Plese ver este doc:

En el modo proxy (que es el valor predeterminado), único método externo llamadas entrantes a través del proxy son interceptados. Esto significa que la auto invocación, en efecto, un método dentro del objeto objetivo que llama a otro método del objeto objetivo, no conducirá a una transacción real en el tiempo de ejecución, incluso si el método invocado está marcado con @Transactional.

Considere el uso del modo AspectJ (consulte el atributo de modo en la tabla a continuación) si espera que las autovocaciones se envuelvan con las transacciones también. En este caso, no habrá un proxy en primer lugar; en su lugar, la clase objetivo será tejida (es decir, se modificará su código de bytes) para convertir @Transactional en el comportamiento de tiempo de ejecución en cualquier tipo de método.

---------------------------- sobre

así, anteras manera es usuario BeanSelfAware

84

La pregunta no es privada ni pública, la pregunta es: ¿cómo se invoca y qué implementación de AOP se usa?

Si usa (predeterminado) Spring Proxy AOP, entonces todas las funcionalidades AOP proporcionadas por Spring (como @Transational) solo se tendrán en cuenta si la llamada pasa por el proxy. - Este es normalmente el caso si el método anotado se invoca desde otro bean.

Esto tiene dos implicaciones:

  • Debido a que los métodos privados no deben ser invocados desde otro bean (la excepción es la reflexión), su @Transactional anotación no se tiene en cuenta.
  • Si el método es público, pero se invoca desde el mismo bean, tampoco se tendrá en cuenta (esta afirmación solo es correcta si se utiliza (por defecto) Spring Proxy AOP).

@see Spring Reference: Chapter 9.6 9.6 Proxying mechanisms

mi humilde opinión, se debe utilizar el modo de aspectj, en lugar de los Proxies primavera, que va a superar el problema. Y los aspectos transaccionales de AspectJ se tejen incluso en métodos privados (verificados para Spring 3.0).

+0

nice (+1) pero tuve que editar algo de ortografía y gramática –

+0

Ambos puntos no son necesariamente ciertos. El primero es incorrecto: los métodos privados * pueden * invocarse de manera reflexiva, pero la lógica de descubrimiento del proxy elige no hacerlo. El segundo punto solo es cierto para los proxies JDK basados ​​en interfaz, pero no para los proxies basados ​​en subclass CGLIB. – skaffman

+0

@skaffman: 1 - hago mi declaración más precisa, 2. Pero el proxy por defecto está basado en la interfaz, ¿no es así? – Ralph

8

Sí, es posible utilizar @Transactional en métodos privados, pero como otros han mencionado, esto no funcionará de la caja. Necesitas usar AspectJ. Me llevó algo de tiempo descubrir cómo hacerlo funcionar. Compartiré mis resultados.

Elegí usar el tejido en tiempo de compilación en lugar del tejido en tiempo de carga porque creo que es una mejor opción general. Además, estoy usando Java 8 por lo que es posible que deba ajustar algunos parámetros.

Primero, agregue la dependencia para aspectjrt.

<dependency> 
    <groupId>org.aspectj</groupId> 
    <artifactId>aspectjrt</artifactId> 
    <version>1.8.8</version> 
</dependency> 

Luego agregar el plugin AspectJ para hacer el tejido de bytecode real en Maven (esto puede no ser un ejemplo mínimo).

<plugin> 
    <groupId>org.codehaus.mojo</groupId> 
    <artifactId>aspectj-maven-plugin</artifactId> 
    <version>1.8</version> 
    <configuration> 
     <complianceLevel>1.8</complianceLevel> 
     <source>1.8</source> 
     <target>1.8</target> 
     <aspectLibraries> 
      <aspectLibrary> 
       <groupId>org.springframework</groupId> 
       <artifactId>spring-aspects</artifactId> 
      </aspectLibrary> 
     </aspectLibraries> 
    </configuration> 
    <executions> 
     <execution> 
      <goals> 
       <goal>compile</goal> 
      </goals> 
     </execution> 
    </executions> 
</plugin> 

Por último agregar esto a su clase de configuración

@EnableTransactionManagement(mode = AdviceMode.ASPECTJ) 

Ahora usted debería ser capaz de utilizar @Transactional en métodos privados.

Una advertencia para este enfoque: Deberá configurar su IDE para conocer AspectJ; de lo contrario, si ejecuta la aplicación a través de Eclipse, es posible que no funcione. Asegúrate de probar contra una construcción directa de Maven como un control de cordura.

Cuestiones relacionadas