Search code examples
javaspringspring-bootaopspring-aop

Spring AOP for Parent and Child relation classes


I'm working on existing code and I am writing some Auditing for the requests. I have tried multiple ways but my Pointcut expressions are not getting executed.

I have created a separate class which has similar annotations without any parent and child hierarchy and I'm able to invoke these pointcut expressions.

I'm not sure what Pointcut expressions should be used when there is Parent and Child relationship.

public interface Parent {
      @GetMapping(value = "/test")
      ResponseEntity<?> createJsonSchemaMetadata();

}

My Child class

@RestController
@Validated
@RequestMapping("/api")
public class Child implements Parent {

    @Override
    public ResponseEntity<?> createJsonSchemaMetadata() {
        return ResponseEntity.status(HttpStatus.OK).body("Successful");
    }
}

My Pointcut expressions:

@Aspect
public class PointcutDefinition {

    // targeting GET,POST,PUT,DELETE methods
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping) "
            + "|| @annotation(org.springframework.web.bind.annotation.PutMapping)"
            + "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)")
    public void httpMethodsWithAnnotation() {
    }

    // targeting methods which are defined using RequestMapping.
    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void requestMappingMethodsWithHttpMethods() {
    }

}

My Advice:

@Aspect
@Component
public class AuditingAspect {

    private static final Logger log = LoggerFactory.getLogger(AuditingAspect.class);

    
    @Around(" PointcutDefinition.httpMethodsWithAnnotation() || PointcutDefinition.requestMappingMethodsWithHttpMethods()")
    public Object auditAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        if (log.isTraceEnabled()) {
            log.trace("Entering auditAdvice method in " + this.getClass());
        }
        System.out.println("Auditing triggered.");
        Object proceed = joinPoint.proceed();
        return proceed;
    }
}

I see my Advice is not getting invoked with these Pointcut expressions. Can someone help here.


Solution

  • (Almost) no annotation inheritance in Java

    M. Deinum is right about annotation inheritance. It basically does not exist in Java with one very special exception. For details, see my answer here. Other than using advanced native AspectJ, as described there, your simplest workaround is not to annotate the interface method but the implementing class's method:

    @Override
    @GetMapping(value = "/test")
    public ResponseEntity<?> createJsonSchemaMetadata() {
      return ResponseEntity.status(HttpStatus.OK).body("Successful");
    }
    

    Targeting annotated classes

    You also want to make sure that no JDK proxies are used for classes overriding interfaces but CGLIB proxies. In Spring Boot, this is the default. In plain vanilla Spring, you can use @EnableAspectJAutoProxy(proxyTargetClass = true).

    Regarding your second pointcut, it is simply wrong:

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    

    This targets annotated methods, but you want to intercept methods in annotated classes, which is different and needs to be expressed like this:

    @Pointcut("@within(org.springframework.web.bind.annotation.RequestMapping)")
    

    If all your target classes carry a @RequestMapping annotation, you do not need the first pointcut at all. Otherwise, you do, but then make sure to follow my advice from the first paragraph.