简体   繁体   中英

How to audit methods in Java Spring Boot

I am writing a Spring Boot Application. I want to audit methods with my annotation @AuditMetod : For example I have method foo() with the annotation:

@AuditMetod(name = "SomeValue")
foo() {...}

I want to handle and audit such methods like this (the simplest example):

auditMethod(Method method) {
    if (method.hasAnnotation(AuditMethod.class)) {
        System.out.println (method.getName() + " was called at " + new Date())
    }
}

upd

Thanks to @Karthikeyan @Swapnil Khante and @misha2048 I understood, that I need to use AOP. But I have 2 problems:

  1. The only method in Aspect class in not being called and I don't see the inscription "----------ASPECT METHOD IS CALLED-----------" in log
  2. How can I check in aspect method what method it is intercepting. To get an instance of Method class.

Now I have the following code: Controller:

@PostMapping
    @LoggingRest(executor = "USER", method = "CREATE", model = "SUBSCRIPTION")
    public ResponseEntity<?> create(@Valid @RequestBody SubscriptionRequestDto dto) {
       ...
    }

Aspect:

`@Aspect
@Slf4j
@Component
public class AuditAspect {
@Pointcut(value = "@annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
    log.info("----------ASPECT METHOD IS CALLED------------");
}`

And annotation:

    @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LoggingRest {
    String executor() default "SYSTEM";
    String method() default "";
    String model() default "";

}

Auditing is a cross-cutting concern and can be handled using AOP.

Another solution would be to use a low-level solution by writing a custom annotation and using a Spring interceptor to write your business logic.

To use the Spring interceptor you will need to implement the HandlerInterceptor interface

Example of the annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Audit {
    boolean active() default true;
}

Interceptor example

@Component
public class AuditInterceptor implements HandlerInterceptor {

@Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
      throws Exception {

    if (handler instanceof HandlerMethod) {

      HandlerMethod handlerMethod = (HandlerMethod) handler;
      Audit annotation = handlerMethod.getMethodAnnotation(Audit.class);

       if (annotation != null && annotation.active()) {
           // your business logic
       }
     }
     HandlerInterceptor.super.afterCompletion(request, response, handler, ex);

}

check this interceptor example

I think one of the solutions here, as @Karthikeyan mentioned, is to use Spring AOP.

If you are not aware a brief introduction - spring-aop module implements the aspect oriented programming paradigm. We extract some common functionality, that we generally want to apply to some subset of functions/methods, to an entity called Aspect (see class annotated with @Aspect ). This class will contain out cross-cutting functionality - such as auditing, for instance we want to audit the methods execution time, lets say. We just put the code to be executed, the condition, which tell the spring what exact beans methods should be affect by this aspect, see below.

For example, if I can audit the method execution duration with the following very simple example (in my case I said that any public method, returning void inside the Class com.example.stackoverflow.BusinessLogicClass must be inspected by this Aspect):

@SpringBootApplication
@EnableAspectJAutoProxy
public class StackoverflowApplication implements ApplicationRunner {

    @Autowired
    private BusinessLogicClass businessLogicClass;

    public static void main(String[] args) {
        SpringApplication.run(StackoverflowApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        businessLogicClass.test();
    }
}

@Aspect
@Component
class MyAspectLogicClass {

    @Around("execution(public void com.example.stackoverflow.BusinessLogicClass.*(..))")
    public Object hangAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long before = System.currentTimeMillis();
        Object returnedValue = proceedingJoinPoint.proceed();
        long after = System.currentTimeMillis();
        System.out.printf("Retruned in '%s' ms %n", (after - before));
        return returnedValue;
    }
}

@Component
class BusinessLogicClass {

    public void test() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

In my case, I will get the time before method execution, then by the means of proceedingJoinPoint.proceed() call I delegate the execution to the real method, and then, once I get the response back, I will get the current system time and calculate the execution time, fairly simple.

I hope I have at least directed you somewhere, if you are looking for documentation, this are the resources I suggest you should look for:

Hope it helped :)

The problem was in right annotation. In Aspect class I tried @Around and everything works as I need.

@Aspect
@Slf4j
@Component
public class AuditAspect {

    @Around(value = "@annotation(com.aspect.annotations.LoggingRest)")
    public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
        var method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        log.info("----------ASPECT METHOD IS CALLED------------");
    }
}

For getting a Method instance I use fallowing code

Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM