Dove devo inserire l'annotazione @Transactional: in una definizione di interfaccia o in una classe di implementazione?


91

La domanda dal titolo in codice:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

vs

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

Risposte:


121

Da http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

La raccomandazione del team di Spring è di annotare solo le classi concrete con l' @Transactionalannotazione, invece di annotare le interfacce. Puoi certamente posizionare l' @Transactionalannotazione su un'interfaccia (o un metodo di interfaccia), ma funzionerà solo come ti aspetteresti se utilizzi proxy basati su interfaccia. Il fatto che le annotazioni non siano ereditate significa che se si utilizzano proxy basati sulla classe, le impostazioni della transazione non verranno riconosciute dall'infrastruttura proxy basata sulla classe e l'oggetto non verrà avvolto in un proxy transazionale (il che sarebbe decisamente negativo ) . Quindi, per favore, segui il consiglio del team di Spring e annota solo le classi concrete (ei metodi delle classi concrete) con l' @Transactionalannotazione.

Nota: poiché questo meccanismo è basato sui proxy, verranno intercettate solo le chiamate di metodo "esterne" in arrivo tramite il proxy. Ciò significa che 'auto-invocazione', cioè un metodo all'interno dell'oggetto di destinazione che chiama un altro metodo dell'oggetto di destinazione, non porterà a una transazione effettiva in fase di esecuzione anche se il metodo invocato è contrassegnato con @Transactional!

(Enfasi aggiunta alla prima frase, altra enfasi dall'originale.)


Grande +1 BTW, mi sono preso la libertà di rendere la citazione una citazione, in modo che le persone non siano confuse, e ho aggiunto il corsivo e simili dall'originale.
TJ Crowder

Ho letto questa dichiarazione in precedenza in Spring docu, ma ancora non riesco a capire perché le impostazioni delle transazioni non verranno riconosciute dall'infrastruttura proxy basata su classi ? Personalmente penso che questa sia solo una limitazione dell'implementazione di Spring, non il problema dell'infrastruttura proxy sottostante.
dma_k

Guardando le fonti Spring si può scoprire che JdkDynamicAopProxypassa attraverso tutti i bean advisor su ogni invocazione di metodo (vedere anche DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()), che in caso di configurazione di transazione dichiarativa, include BeanFactoryTransactionAttributeSourceAdvisor. A sua volta TransactionAttributeSourcePointcut#matches()viene invocato, che dovrebbe raccogliere le informazioni relative alla transazione. Viene passata alla classe di destinazione e può sempre attraversare tutte le interfacce implementate da questa classe. Ora: perché questo non può non funzionare in modo affidabile?
dma_k

2
@dma_k - Il runtime Java fornisce solo accesso diretto alle annotazioni di classe ereditate da superclassi o annotazioni di metodo senza alcuna ereditarietà. Quindi Pointcut.matches () dovrebbe attraversare ricorsivamente tutte le interfacce, trovare il metodo "reale" sovrascritto con la reflection, gestire i metodi bridge, sovraccaricare, vararg, generics, proxy e, naturalmente, farlo velocemente . Quindi devi occuparti dell'eredità del diamante: quale delle molte @Transactionalannotazioni ereditate si applicherebbe? Quindi, sebbene sia tutto possibile , non biasimo la primavera. jira.springsource.org/browse/SPR-975
Peter Davis

9

Puoi inserirli nell'interfaccia ma tieni presente che le transazioni potrebbero non essere eseguite in alcuni casi. Vedi il secondo suggerimento nella sezione 10.5.6 dei documenti di primavera:

Spring consiglia di annotare solo le classi concrete (e i metodi delle classi concrete) con l'annotazione @Transactional, anziché le interfacce di annotazione. Puoi sicuramente posizionare l'annotazione @Transactional su un'interfaccia (o un metodo di interfaccia), ma funziona solo come ti aspetteresti se utilizzi proxy basati su interfaccia. Il fatto che le annotazioni Java non siano ereditate dalle interfacce significa che se stai utilizzando proxy basati sulla classe (proxy-target-class = "true") o l'aspetto basato sulla tessitura (mode = "aspectj"), le impostazioni della transazione sono non riconosciuto dall'infrastruttura di proxy e tessitura e l'oggetto non verrà avvolto in un proxy transazionale, il che sarebbe decisamente negativo.

Consiglierei di inserirli nell'implementazione per questo motivo.

Inoltre, a me, le transazioni sembrano un dettaglio di implementazione, quindi dovrebbero essere nella classe di implementazione. Immagina di avere implementazioni wrapper per la registrazione o implementazioni di test (mock) che non devono essere transazionali.


9

Il consiglio di Spring è di annotare le implementazioni concrete invece di un'interfaccia. Non è sbagliato usare l'annotazione su un'interfaccia, è solo possibile abusare di quella funzione e bypassare inavvertitamente la dichiarazione @Transaction.

Se hai contrassegnato qualcosa di transazionale in un'interfaccia e poi fai riferimento a una delle sue classi di implementazione altrove in primavera, non è abbastanza ovvio che l'oggetto creato da Spring non rispetterà l'annotazione @Transactional.

In pratica assomiglia a questo:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

1

Supportare @Transactional sulle classi concrete:

Preferisco progettare una soluzione in 3 sezioni in generale: un'API, un'implementazione e un Web (se necessario). Faccio del mio meglio per mantenere l'API il più leggera / semplice / POJO possibile riducendo al minimo le dipendenze. È particolarmente importante se lo giochi in un ambiente distribuito / integrato in cui devi condividere molto le API.

Mettere @Transactional richiede le librerie Spring nella sezione API, che IMHO non è efficace. Quindi preferisco aggiungerlo nell'implementazione in cui è in esecuzione la transazione.


0

Metterlo sull'interfaccia va bene fintanto che tutti gli implementatori prevedibili del tuo IFC si preoccupano dei dati TX (le transazioni non sono problemi che affrontano solo i database). Se al metodo non interessa TX (ma devi metterlo lì per Hibernate o qualsiasi altra cosa), mettilo su impl.

Inoltre, potrebbe essere un po 'meglio inserire @Transactionali metodi nell'interfaccia:

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.