Let’s assume we have to pass some parameters to the aspect that intercept method invocations. It may be for example the level of logging (DEBUG, INFO, WARNING and so on), format (plain text, JSON, XML), name of the web service.

@LogIt(level = INFO)

or

@LogThis(format = JSON)

or

@LogThat(service = BOOK_SERVICE)

Let’s implement one of them - @LogThat.

We’re going to modify our solution in Logging service client calls with AspectJ

Log annotation with parameters

First we need to add a parameter Service service to our annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogThat {
    Service service();
}

public enum Service {
    BOOK_SERVICE("BOOK SERVICE"),
    CUSTOMER_SERVICE("CUSTOMER SERVICE"),
    INVENTORY_SERVICE("INVENTORY SERVICE");

    public String name;

    Service(String name) {
        this.name = name;
    }
}

Doing that we don’t need to have separate annotations like @LogBookService, @LogCustomerService and @LogInventoryService for each service anymore. We just pass a service as a parameter.

Accessing annotation parameters

Now we can access that parameter from an aspect just like this

((MethodSignature) jp.getSignature()).getMethod().getAnnotation(LogThat.class).service()

Logger aspect

Finally in our ServiceLogger aspect we have one @Around for all services

@Aspect
@Component
public class ServiceLogger {

    private Gson gson = new GsonBuilder().setPrettyPrinting().create();

    @Around("execution(* *(..)) && @annotation(io.lenar.examples.spring.clients.log.LogThat)")
    public Object logServiceMethods(ProceedingJoinPoint jp) throws Throwable {
        String methodName = jp.getSignature().getName();
        logRequest(jp);

        long startTime = new Date().getTime();
        Object result = jp.proceed(jp.getArgs());
        long endTime = new Date().getTime();

        Reporter.log("\nResponse time: " + (endTime - startTime) + "ms", true);
        Reporter.log("<- " + methodName + " Response: \n" + gson.toJson(result) + "\n", true);

        return result;
    }

    private void logRequest(ProceedingJoinPoint jp) {
        String serviceName = ((MethodSignature) jp.getSignature()).getMethod()
                                                  .getAnnotation(LogThat.class).service();
        Reporter.log(serviceName + " CALL:", true);
        String[] argNames = ((MethodSignature) jp.getSignature()).getParameterNames();
        Object[] values = jp.getArgs();
        Map<String, Object> params = new HashMap<>();
        if (argNames.length != 0) {
            for (int i = 0; i < argNames.length; i++) {
                params.put(argNames[i], values[i]);
            }
        }

        Reporter.log("-> " + jp.getSignature().getName() + " Request", true);
        if (!params.isEmpty()) Reporter.log(gson.toJson(params), true);
    }
}

Aspect weaving

The last thing we need to setup is pom.xml to properly weave our aspects.

There are some differences between Spring and non-Spring projects so carefully read this - Aspect weaving in Spring and non-Spring projects


You may also find these posts interesting: