2010-08-06 15 views
67

Sono nuovo alla transazione di primavera. Qualche cosa che ho trovato davvero strano, probabilmente l'ho capito bene. Volevo avere una transazione attorno al livello del metodo e ho un metodo chiamante all'interno della stessa classe e sembra che non gli piaccia, deve essere chiamato dalla classe separata. Non capisco come sia possibile. Se qualcuno ha un'idea su come risolvere questo problema, sarei molto grato. Vorrei usare la stessa classe per chiamare il metodo transazionale annotato.La chiamata di metodo Spring @Transaction con il metodo all'interno della stessa classe non funziona?

Ecco il codice:

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); 
     } 
    } 
} 

risposta

70

Si tratta di una limitazione con la Primavera AOP. (oggetti dinamici e CGLIB)

Se si configura Spring per utilizzare AspectJ per gestire le transazioni, il codice funzionerà.

L'alternativa semplice e probabilmente migliore è quella del refactoring del codice. Ad esempio una classe che gestisce gli utenti e una che elabora ciascun utente. Funzionerà quindi la gestione della transazione predefinita con Spring AOP.

suggerimenti di configurazione per la gestione delle operazioni con AspectJ

Per consentire primavera da utilizzare AspectJ per le transazioni, è necessario impostare la modalità di AspectJ:

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

Se stai usando primavera con un vecchio versione 3.0, è necessario aggiungere questo alla configurazione di Spring:

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

Grazie per l'informazione. Ho rifattorizzato il codice per ora, ma potresti mandarmi un esempio usando AspectJ o fornirmi qualche link utile. Grazie in anticipo. Mike. – Mike

+0

Aggiunta configurazione AspectJ specifica della transazione nella mia risposta. Spero possa essere d'aiuto. – Espen

+1

Grazie Espen per tutto il tuo aiuto. Funziona!! – Mike

42

Il problema qui è , i proxy AOP di Spring non si estendono ma racchiudono l'istanza del servizio per intercettare le chiamate. Ciò ha l'effetto che qualsiasi chiamata a "this" dall'interno dell'istanza del servizio viene invocata direttamente su quell'istanza e non può essere intercettata dal proxy wrapping (il proxy non è nemmeno a conoscenza di tale chiamata). Una soluzione è già menzionata. Un altro elegante sarebbe semplicemente avere Spring iniettare un'istanza del servizio nel servizio stesso e chiamare il metodo sull'istanza iniettata, che sarà il proxy che gestisce le transazioni. Ma essere consapevoli che questo può avere effetti collaterali negativi anche se il chicco di servizio non è un singleton:

<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

Se si sceglie di percorrere questa rotta (se questa è una buona progettazione o meno è un'altra questione) e non utilizzare l'iniezione del costruttore, assicurarsi di vedere anche [questo domanda] (http://stackoverflow.com/questions/5152686/self-injection-with-spring) – Jeshurun

0

È possibile autowired BeanFactory all'interno della stessa classe e fare un

getBean(YourClazz.class)

E ' eseguirà automaticamente il proxify della tua classe e prenderà in considerazione la tua annotazione @Transactional o altra aop.

+2

È considerata una cattiva pratica: anche iniettare ricorsivamente il bean in se stesso è meglio. L'uso di getBean (clazz) è un problema accoppiamento e forte dipendenza dalle classi ApplicationContext primarie all'interno del tuo codice. Anche ottenere il bean per classe potrebbe non funzionare in caso di spring wrapping del bean (la classe può essere cambiata). –

5

Questa è la mia soluzione per l'auto invocazione.

private SBMWSBL self; 
@Autowired private ApplicationContext applicationContext; 

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