(Spring) AOP Design of Transactions

1

Nella nostra applicazione abbiamo diverse transazioni, in cui ogni passaggio viene registrato estesamente.

Pseudocodice:

@Component
public class OurServiceImpl implements OurService {

...
    @Transactional
    public void doComplexTransaction(...){
      String transactionId = generateTransactionId(userid, date)
      log.info("==========================Start Transaction" + transactionId);

      log.info("Doing A");
      doA(..., transcationId);

      log.info("Doing B");
      doB(..., transcationId);

      log.info("Doing C");
      doC(... transcationId);

      log.info("==========================End Transaction" + transactionId);
    }

...

    public void doA(..., transactionId){

      ...

      log.info("Doing SubA "+transactionId);
      doSubA();
    }

}

Poiché la registrazione è fonte di distrazione, abbiamo deciso di utilizzare AOP per questo. Il metodo doComplexTransaction(..) potrebbe essere coperto con @Around(execution(FQN.doComplexTransaction(..)))

Le chiamate doA() , doB() e doC() potrebbero essere rielaborate in un'altra classe, quindi potrebbero essere coperte con @Around allo stesso modo.

Ma poi si verifica il seguente problema:

Come passare transcationId (generato in un consiglio) tra diversi consigli ?

O più generale:

Come progettare "logging transazionale (con identificatore)" su più richiami di metodo e oggetti?

Potrebbe essere fatto con Spring-AOP ?

Il mio obiettivo è discernere le diverse transazioni simultanee nel mio log, il che renderebbe facile l'interrogazione tramite elasticsearch et al.

Bruteforce-soluzione:

(simile a Steve Park's)

Un'idea, che avevo, stava generando il transactionId nel primo consiglio e la "inietta" per ulteriori chiamate in proceedingJoinPoint.proceed(..., transactionId). In any further advice, I could extract the transactionId via .getArgs () and inject it in the proceed () '- call.

Ma questo ha due aspetti negativi:

1) È esteticamente non piacevole o semplicemente: scomodo. È un hack, anche se funzionante.

2) Ciò viola diversi principi di progettazione, in primo luogo: il principio del minimo stupore . Se un recensore guarda il POJO e vede una variabile passata ma mai "usata" - grazie ad AOP - sarebbe almeno stupito .

Quindi, sì, quella è una soluzione, ma sto cercando una soluzione migliore.

    
posta Thomas Junk 23.06.2015 - 15:13
fonte

1 risposta

2

Non sono sicuro di comprendere appieno la tua intenzione o meno, ma non hai bisogno di passare transactionId tra i consigli se vuoi solo ottenere il transactionId con un nuovo consiglio, dato che puoi ottenerlo dall'oggetto JoinPoint. Se presumo che tu imposti un nuovo consiglio di Around per coprire i punti di join (doA, doB e doC sul nuovo servizio), tutti gli argomenti di input su quei punti di join (doA, doB e doC) sono disponibili dall'oggetto JoinPoint tramite il metodo getArgs () come sotto Quindi, è sufficiente iterare gli argomenti di input con il tipo di istanza di controllo o l'ordine dell'argomento di input per trovare il tipo di stringa transactionId. Ho semplicemente refactato doA, doB e doC su AnotherSerivce per usare Around advice.

ourservice

@Service("ourService")
public class OurServiceImpl implements OurService {
    static Logger log = Logger.getLogger(OurServiceImpl.class.getName());

    @Autowired
    private AnotherService anotherService;

    @Override
    public void doComplexTransaction() {
        String transactionId = generateTransactionId(userid, date);
        anotherService.doA(transactionId);
        anotherService.doB(transactionId);
        anotherService.doC(transactionId);
    }

Il servizio contiene i metodi doA, doB e doC

@Service("anotherService")
public class AnotherServiceImpl implements AnotherService {
    static Logger log = Logger.getLogger(AnotherServiceImpl.class.getName());

    @Override
    public void doA(String transactionId) {
        // TODO Auto-generated method stub
        log.info("test inside of doA");
    }

    @Override
    public void doB(String transactionId) {
        // TODO Auto-generated method stub
        log.info("test inside of doB");
    }

    @Override
    public void doC(String transactionIds) {
        // TODO Auto-generated method stub
        log.info("test inside of doC");
    }
}

Aspetto per tracciare doA, doB e doC

@Aspect
@Component
public class TransactionTracker {

    static Logger log = Logger.getLogger(TransactionTracker.class.getName());

    @Around(value="execution(* com.my.AnotherServiceImpl.do*(..)) " )
    public void aroundProcess(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        for(Object arg: args) {
            if(arg instanceof String) {
                log.info("Get string type input argument: ".concat(arg.toString()));
            }
        }
        try {
            joinPoint.proceed(args);
        } catch(Exception e) {
            log.error(e.getMessage());
            throw e;
        }
    }

    @Around(value="execution(* com.my.OurServiceImpl.do*(..)) " )
    public void aroundProcessForOurService(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        log.info("start transaction");

        try {
            joinPoint.proceed(args);
        } catch(Exception e) {
            log.error(e.getMessage());
            throw e;
        }
        log.info("end transaction");
    }
}

file di configurazione dell'app

@Configuration
@EnableAutoConfiguration
@EnableAspectJAutoProxy
@ComponentScan("com.my")
public class AppConfig {

}
    
risposta data 23.06.2015 - 22:48
fonte

Leggi altre domande sui tag