2010-08-06 26 views
67

Soy nuevo en Spring Transaction. Algo que encontré realmente extraño, probablemente entendí esto correctamente. Quería tener un nivel de método transaccional y tengo un método de llamada dentro de la misma clase y parece que no le gusta, tiene que ser llamado desde la clase separada. No entiendo cómo es eso posible. Si alguien tiene una idea de cómo resolver este problema, lo agradecería enormemente. Me gustaría utilizar la misma clase para llamar al método transaccional anotado.La llamada al método Spring @Transaction por el método dentro de la misma clase, ¿no funciona?

Aquí está el código:

public class UserService { 

    @Transactional 
    public boolean addUser(String userName, String password) { 
     try { 
      // call DAO layer and adds to database. 
     } catch (Throwable e) { 
      TransactionAspectSupport.currentTransactionStatus() 
        .setRollbackOnly(); 

     } 
    } 

    public boolean addUsers(List<User> users) { 
     for (User user : users) { 
      addUser(user.getUserName, user.getPassword); 
     } 
    } 
} 

Respuesta

70

Es una limitación con Spring AOP. (objetos dinámicos y CGLIB)

Si configura Spring para usar AspectJ para manejar las transacciones, su código funcionará.

La alternativa más simple y probablemente la mejor es refactorizar su código. Por ejemplo, una clase que maneja usuarios y otra que procesa a cada usuario. Entonces el manejo predeterminado de las transacciones con Spring AOP funcionará.

consejos de configuración para el manejo de transacciones con AspectJ

Para habilitar la primavera de usar AspectJ para las transacciones, debe establecer el modo de AspectJ:

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

Si está utilizando la primavera con un mayor versión 3.0, también debe agregar esto a su configuración de Spring:

<bean class="org.springframework.transaction.aspectj 
     .AnnotationTransactionAspect" factory-method="aspectOf"> 
    <property name="transactionManager" ref="transactionManager" /> 
</bean> 
+0

Gracias por la información. Refactoré el código por el momento, pero podría enviarme un ejemplo utilizando AspectJ o proporcionarme algunos enlaces útiles. Gracias por adelantado. Micro. – Mike

+0

Agregué la configuración específica de AspectJ a la transacción en mi respuesta. Espero que ayude. – Espen

+1

Gracias Espen por toda su ayuda. ¡¡Funciona!! – Mike

42

El problema aquí es , que los proxies AOP de Spring no se extienden, sino que envuelven su instancia de servicio para interceptar llamadas. Esto tiene el efecto de que cualquier llamada a "esto" desde su instancia de servicio se invoca directamente en esa instancia y no puede ser interceptada por el proxy de envoltura (el proxy ni siquiera es consciente de dicha llamada). Una solución ya se menciona. Otro ingenioso sería simplemente hacer que Spring inserte una instancia del servicio en el servicio mismo, y llame a su método en la instancia inyectada, que será el proxy que maneja sus transacciones. Pero tenga en cuenta, que esto puede tener efectos secundarios perjudiciales, si su grano de servicio no es un producto único:

<bean id="userService" class="your.package.UserService"> 
    <property name="self" ref="userService" /> 
    ... 
</bean> 

public class UserService { 
    private UserService self; 

    public void setSelf(UserService self) { 
     this.self = self; 
    } 

    @Transactional 
    public boolean addUser(String userName, String password) { 
     try { 
     // call DAO layer and adds to database. 
     } catch (Throwable e) { 
      TransactionAspectSupport.currentTransactionStatus() 
       .setRollbackOnly(); 

     } 
    } 

    public boolean addUsers(List<User> users) { 
     for (User user : users) { 
      self.addUser(user.getUserName, user.getPassword); 
     } 
    } 
} 
+2

Si elige seguir esta ruta (si esto es un buen diseño o no es otro asunto) y no use la inyección de constructor, asegúrese de que también vea [este pregunta] (http://stackoverflow.com/questions/5152686/self-injection-with-spring) – Jeshurun

0

Puede autowired BeanFactory dentro de la misma clase y hacer un

getBean(YourClazz.class)

Se automáticamente proxificará tu clase y tendrá en cuenta tu @Transactional u otra anotación aop.

+2

Se considera una mala práctica. Incluso inyectar el bean recursivamente en sí mismo es mejor. Usar getBean (clazz) es un poco apretado. acoplamiento y una fuerte dependencia en las clases de Spring ApplicationContext dentro de su código. También obtener beans por clase puede no funcionar en caso de que la primavera envuelva el bean (la clase puede ser cambiada). –

5

Esta es mi solución para la auto invocación.

private SBMWSBL self; 
@Autowired private ApplicationContext applicationContext; 

@PostConstruct 
public void postContruct(){ 
    self =applicationContext.getBean(SBMWSBL.class); 
} 
Cuestiones relacionadas