2

I have a scenario where my method to be intercepted is in the parent class and is not overridden in the pointcut class. Here is the sample classes:

public abstract class A{
@RequestMapping(value = "/data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getData(@RequestBody String request) throws Exception {
    return "dummy";
}
}

@RestController
public class B extends A {
}

My Aspect is defined as:

@Aspect
@Component
public class RestCallLogger {
    @Pointcut("within(com.test..*) && within(@org.springframework.web.bind.annotation.RestController *)")
    public void restControllers() {
    }

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void requestMappingAnnotations() {
    }

    @Around("restControllers() && requestMappingAnnotations()")
    public Object onExecute(ProceedingJoinPoint jp) throws Throwable {
        Object result = null;
        try {
            result = jp.proceed();
        } catch (Exception ex) {
            throw ex;
        }
        return result;
    }
}

But its not working. If I mark class A with Annotation @RestController and make it concrete, then it works. The question is how can I create a "pointcut for method in parent abstract class"? PS: I can not change the hierarchy of the code as its the existing code.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
Vishal
  • 123
  • 3
  • 14
  • 2
    I don't think it will work for abstract class, as aop for intercept method invocation. We can't create object of abstract class. So you should define pointcut for concrete ones. – Gaurav Srivastav Jun 15 '18 at 12:40
  • For a pointcut, u need an underlying implementation of your declaring abstraction. Aspect will create a pointcut around/before.... it and then it will first create a proxyObject. Each proxy instance has an associated invocation handler object, which implements the interface `InvocationHandler.` which will be used to invoke method of the instance's invocation handler. More on this https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html – Sagar Kharab Jun 15 '18 at 12:49

1 Answers1

2

For me this works. Here is an MCVE:

package de.scrum_master.app;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

public abstract class A {
  @RequestMapping(value = "/data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
  public String getData(@RequestBody String request) throws Exception {
    return request;
  }
}
package de.scrum_master.app;

import org.springframework.web.bind.annotation.RestController;

@RestController
public class B extends A {}
package de.scrum_master.app;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan(basePackages = { "de.scrum_master" })
public class Application2 {
  public static void main(String[] args) throws Exception {
    ApplicationContext appContext = new AnnotationConfigApplicationContext(Application2.class);
    B b = (B) appContext.getBean("b");
    System.out.println(b.getData("bbb"));
    A a = (A) appContext.getBean("b");
    System.out.println(a.getData("aaa"));
  }
}
package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class RestCallLogger {
  @Pointcut("within(de.scrum_master..*) && @target(org.springframework.web.bind.annotation.RestController)")
  public void restControllers() {}

  @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
  public void requestMappingAnnotations() {
  }

  @Around("restControllers() && requestMappingAnnotations()")
  public Object onExecute(ProceedingJoinPoint jp) throws Throwable {
    System.out.println(jp);
    Object result = null;
    try {
      result = jp.proceed();
    } catch (Exception ex) {
      throw ex;
    }
    return result;
  }
}

The console log says:

execution(String de.scrum_master.app.A.getData(String))
bbb
execution(String de.scrum_master.app.A.getData(String))
aaa

What is different in your case?

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • I missed to add the "restControllers()" in the Around advice. So the Around advice is `@Around("restControllers() && requestMappingAnnotations()")` which doesn't work. The question is updated now. Thanks – Vishal Jun 18 '18 at 04:12
  • 1
    Adding crucial information after the question has been answered is kinda suboptimal, but I looked into it again and updated the aspect. It works if you use `@target()` pointcut designator. BTW, there was also a little typo in my driver application: `System.out.println(b.getData("aaa"));` should be changed to `System.out.println(a.getData("aaa"));`, of course. With meaningless class names things like that can happen sometimes. – kriegaex Jun 19 '18 at 00:51
  • 1
    @kriegaex Did you forget to include `restControllers()` pointcut in the `@Around` specification? With the current example the `restControllers()` pointcut is defined but never used. – Ruslan Stelmachenko Oct 15 '20 at 12:00
  • Thanks for the feedback, @RuslanStelmachenko. Good catch! This question being 2+ years old, I had to find it on my hard disk first, and oddly enough there in my sample project the pointcut is correct. I don't know why here it was not. I updated the answer. – kriegaex Oct 16 '20 at 03:31