Konrad Garus
Konrad Garus

Reputation: 54005

Avoid nested calls

Let's say I have an aspect wrapper on all public methods of my services which detaches entities from database before returning them to controller:

@Around("execution(public * *(..)) && @within(org.springframework.stereotype.Service)")

When one service is calling another directly, this wrapper is being triggered as well. For instance:

@Service
class ServiceA {
    @Autowired
    ServiceB b;

    public void foo() {
        b.bar();
    }
}

@Service
class ServiceB {
    public void bar() {
    }
}

When I call ServiceA.foo(), the wrapper is triggering around the nested call to bar() as well.

It should trigger around the call to foo(), but not bar(). How can I avoid this?

Upvotes: 2

Views: 421

Answers (2)

kriegaex
kriegaex

Reputation: 67297

I am on the road with my iPad only, so I cannot test it now, but you could try something along the lines of:

pointcut myCalls() :
    execution(public * *(..)) && @within(org.springframework.stereotype.Service);
pointcut myCallsNonRecursive() :
    myCalls() && !cflowbelow(myCalls())

around() : myCallsNonRecursive() {
    // ...
}

Sorry for the AspectJ native syntax, I am just more familiar with it.

Upvotes: 0

sinuhepop
sinuhepop

Reputation: 20297

I've sometimes resolved this kind of problems using ThreadLocal variables. Try something like:

@Aspect
public class DetacherAspect {

    private final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();

    @Around("execution(public * *(..)) && @within(org.springframework.stereotype.Service)")
    public Object execute(ProceedingJoinPoint pjp) throws Throwable {

        boolean isNested = threadLocal.get() != null;
        if (!isNested) {
            // Set some object (better create your own class) if this is the first service
            threadLocal.set(new Object());
        }

        try {


            ... // Your aspect


        } finally {

            // Clean thread local variables
            if (!isNested) {
                threadLocal.remove();
            }

        }

    }

}

Obviously, this will only work when all calls are done in the same thread. Thread local variables have some other drawbacks too and is good to read about them.

Upvotes: 1

Related Questions