Reputation: 51
I am using Spring AOP in my code to intercept the execution of a certain method. A simplified example of what I'm trying to do is below:
public void someMethod() {
//does something
}
@Around("execution( someMethod())")
public void anotherMethod(final ProceedingJoinPoint joinPoint) {
//i want to add this to a queue to get executed later on
addToWaitList(new Callable() {
@Override
public call() throws Exception {
joinPoint.proceed();
}
});
return;
}
Essentially, I want to hold off the execution of someMethod()
until it is at the head of the list. However, the main thread blocks, even though I return at the end of anotherMethod()
, so I am unable to add a new Callable
to the list until the first one is done executing.
The documentation says you can shortcut the advised method execution by returning its own return value or throwing an exception. I don't want to throw an exception and am not really sure what "returning its own return value" means in this case. I want to be able to add the Callables to the list with the main thread and then have some other threadpool executing them.
Upvotes: 5
Views: 6438
Reputation: 67297
What you wish to implement is the worker object pattern. I have created a small example for you showing how you can intercept method calls with by a certain naming pattern, but with variable return types and parameters. See my own answer there for a more complex example.
Driver application:
public class Application {
public static void main(String[] args) {
System.out.println("Starting up application");
Application app = new Application();
app.doThis(11);
app.doThat();
app.doThis(22);
System.out.println("Shutting down application");
}
public void doThis(int number) {
System.out.println("Doing this with number " + number);
}
public String doThat() {
String value = "lorem ipsum";
System.out.println("Doing that with text value '" + value + "'");
return value;
}
}
Aspect implementing worker object pattern:
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Callable;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class QueuedExecutionAspect {
Queue<Callable<Object>> waitList = new LinkedList<Callable<Object>>();
private void addToWaitList(Callable<Object> callable) {
waitList.add(callable);
}
@Around("execution(public * Application.do*(..))")
public Object anotherMethod(final ProceedingJoinPoint joinPoint) {
System.out.println(joinPoint + " -> adding to execution queue");
addToWaitList(new Callable<Object>() {
@Override
public Object call() throws Exception {
try {
joinPoint.proceed();
} catch (Throwable e) {
throw new Exception(e);
}
return null;
}
});
return null;
}
@After("execution(public * Application.main(..))")
public void doDelayedExecution(JoinPoint joinPoint) throws Exception {
System.out.println("\nDelayed executions:");
while (!waitList.isEmpty()) {
waitList.poll().call();
}
}
}
Output:
Starting up application
execution(void Application.doThis(int)) -> adding to execution queue
execution(String Application.doThat()) -> adding to execution queue
execution(void Application.doThis(int)) -> adding to execution queue
Shutting down application
Delayed executions:
Doing this with number 11
Doing that with text value 'lorem ipsum'
Doing this with number 22
As you can see from the output, the @Around
advice terminates normally after having added the Callable
worker object to the queue, application execution continues without proceed()
having been called. For illustration I added another advice which runs all elements from the FIFO queue (other queue types can be used according to your needs) before the application exits.
Upvotes: 4