2
votes

I have a simple aspect (see below) with @Around annotation. This aspect works when the the application don't use reactive paradigm. But when the application returns Mono or Flux doesn't works properly.

I need to get the object returned from the method to generate a JSON object to use as log, generate events, etc.

Here is my code that works in a non reactive classes:

@Around("@annotation(simpleEvent)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint, SimpleEvent simpleEvent) throws Throwable {
    final long start = System.currentTimeMillis();
    Object proceed = null;
    try {
        proceed = joinPoint.proceed();
        // here in the real life the object that transformed in json to do others actions
        System.out.println(proceed);
        final long executionTime = System.currentTimeMillis() - start;
        System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        return proceed;
    } catch (Exception e) {
        e.printStackTrace(); 
    }
    return proceed;
}

How to get the object returned from joinPoint.proceed() when is Mono or Flux?

Thanks in advance.

2
A Flux or a Mono represent a deferred computation. Such a method usually immediately returns, with an "inert" Mono which will be triggered once something calls one of its subscribe methods, so that kind of aspect isn't adapted anymore.Simon Baslé
Hi Simon, first of all, thanks for your answer. Sorry but I am new with Spring reactive. In this case it's possible to subscribe to receive the events returned from Flux or Mono too? I use this aspect to throw a business event. There are another approach with reactive style? If yes you can point me in a correct way? Thanks.Maicon Luz
You could change the returned Mono and add a side effect to it. Look at the doOn* methods, which don't change the type of the Mono but add behavior triggered by various events. The business logic shod be non blockingSimon Baslé

2 Answers

3
votes

you can do like this, same when proceed return a Mono

@Around("somePointCut()")
public Object aspectForSomething(ProceedingJoinPoint point) throws Throwable {
    Flux flux = (Flux) point.proceed();
    return flux
            .doOnNext(obj -> {
                log.error("just take a look: " + obj);
            })
            .map(obj -> {
                if (obj instanceof SomeBo) {
                    SomeBo bo = (SomeBo) obj;
                    bo.setName("do some modification");
                    return bo;
                } else {
                    return obj;
                }
            });
}
2
votes

The key is to wrap the joinPoint.proceed() in Mono and access it in a reactive chain.

@Around("@annotation(simpleEvent)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint, SimpleEvent simpleEvent) throws Throwable {
    final long start = System.currentTimeMillis();
    Mono proceed = null;
    try {
        proceed = (Mono) joinPoint.proceed();

        return proceed.doOnNext(response -> {
            final long executionTime = System.currentTimeMillis() - start;
            // here you can access the response object and do your actions
            System.out.println(response);
            System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
    return proceed;
}