Search code examples
javaspringaopguiceaspectj

Use an aspect written for spring application in guice application


I had written an aspect as part of an application using Spring AOP/AspectJ annotations similar to below aspect:

@Aspect
@Component
public class LoggingAspect {
    @Around("@annotation(loggable)")
    public Object log(final ProceedingJoinPoint joinPoint, final Loggable loggable) throws Throwable {
        //log method arguments
        try {
            Object returnValue = joinPoint.proceed();
            // log return value
            return returnValue;
        } catch (Exception ex) {
            // publish exception metrics to some other system
            throw ex;
        }
    }
}

Now I want to use this same aspect in another project, but this project uses Guice instead of Spring.

I was reading about Guice AOP which requires aspect to implement the MethodInterceptor interface and thus I will need to implement the below method:

Object invoke(MethodInvocation methodInvocation) throws Throwable;

What I was thinking was to modify the already existent aspect to implement the MethodInterceptor and internally call the log method. Something like below:

@Aspect
@Component
public class LoggingAspect implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // call already defined log method, but that method expects a ProceedingJoinPoint, however
        // I get MethodInvocation as input parameter in this method
    }

// already defined log method
@Around("@annotation(loggable)")
    public Object log(final ProceedingJoinPoint joinPoint, final Loggable loggable) throws Throwable {
......
.....
}

But due to incompatible type between two methods, I am unable to proceed.

Is there a way I can reuse the existing code instead of writing a brand new aspect with duplicate code to support Guice?


Solution

  • If I understand correctly, you want to reverse the control flow, which can be done with callbacks.

    @Aspect
    @Component
    class LoggingAspect implements MethodInterceptor {
        @Around("@annotation(loggable)")
        public Object log(final ProceedingJoinPoint joinPoint, final Loggable loggable) throws Throwable {
            return log(joinPoint::getArgs, () -> joinPoint.proceed(joinPoint.getArgs()));
        }
        
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            return log(methodInvocation::getArguments, methodInvocation::proceed);
        }
    
        public Object log(Supplier<Object[]> arguments, Supplier<Object[]> proceed) {
            Object[] args = arguments.get();
            //log method arguments
            try {
                Object returnValue = proceed.get();
                // log return value
                return returnValue;
            } catch (Exception ex) {
                // publish exception metrics to some other system
                throw ex;
            }
        }
    
    }
    

    BTW do you intentionally catch only Exception and not Throwable? Errors would not be logged.