0

I've been browsing the web and stackoverflow for hours now and I can't seem to find a solution to my problem. My guess is that it's something tiny but hopefully obvious to those more proficient with AOP than I am.

We're using Spring Boot 1.2.6.RELEASE for our application and I want to include a custom annotation called @Prohibited in order to be able to execute an @Around advice according to the user's permission settings and the annotation's values.

So far, I only have a blank annotation and I want my advice to be executed around any method annotated with @Prohibited.

The annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface Prohibited {
}

The aspect:

@Aspect
@Component
public class PermissionAspect {

    @Pointcut("execution(@com.whatever.commons.logic.permission.Prohibited * *(..))")
    public void prohibited() {}

    @Around("prohibited()")
    public Object permit(final ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("########### - " + joinPoint.getSignature().getName());
        joinPoint.proceed();
        return null;
    }
}

The annotated method:

@Component
public class AnyService {

    @Prohibited
    public List<Object> anyMethod() {
        //anything - assume a breakpoint here
    }
}

In my pom.xml I have included the spring-boot-starter-aop dependency:

<!-- spring boot with aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

And finally in the application.properties file I have added the following line in order to enable Spring to create proxies around classes, too, and not just interfaces:

spring.aop.proxy-target-class=true

Now I set a breakpoint to the first line of the annotated method and would expect to see at least the print out from my advice in the console, but it never happens.

Instead the application suspends at the breakpoint without any lines printed. Interestingly enough, I can see in the call stack that there is indeed a proxy created for the class and I can see things such as:

CglibAopProxy$CglibMethodInvocation.invokeJoinpoint() line: 717
CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 157

If I comment out the @Prohibited annotation, no proxy is generated at all, so it appears that while Spring does create a CGLIB proxy around my designated class (apparently due to the @Pointcut defined in my aspect), but it does not execute my @Around advice, yet instead just proceeds to the joinpoint.

Does anyone have an idea what I am missing here? Should I work with compile-time weaving (I'd like to avoid that, if possible)?

* UPDATE *

What I forgot to mention was how the method is actually called. From another service (aka a controller in this case):

@RestController
public class AnyController {

    @Autowired
    AnyService anyService;

    @RequestMapping("/this/that")
    public ResponseEntity<Object> something() {
        this.anyService.anyMethod();
    }
}

The problem was that I annotated the wrong method (see answer below).

Serg Derbst
  • 444
  • 6
  • 18
  • 1
    And how is this method called? On the created instance or on a fresh instance of the service? – M. Deinum Nov 30 '15 at 15:00
  • The service is being autowired into another component and called from there. So, I am not instantiating it myself if that's what you mean. ;) – Serg Derbst Nov 30 '15 at 15:04
  • 1
    This works fine for me. Please post a complete example, an MCVE. – Sotirios Delimanolis Nov 30 '15 at 16:20
  • Thanks for your comments, both of you. I figured it out (see my answer below). – Serg Derbst Nov 30 '15 at 16:32
  • Note how we asked for more details. Please edit your question to include those. Otherwise, there's no way to get to the answer you've provided. We don't like to guess. – Sotirios Delimanolis Nov 30 '15 at 16:37
  • Done. Again thank you, I just figured out that the problem was exactly what M. Deinum referred to. – Serg Derbst Nov 30 '15 at 16:47
  • It's still incomplete. Update your `AnyService` in your question. It still looks like you're calling the appropriate method. Here's what an [MCVE](http://stackoverflow.com/questions/21437699/outofmemoryerror-when-seemingly-unrelated-code-block-commented-out) is. – Sotirios Delimanolis Nov 30 '15 at 16:49
  • I think my question and answer are pretty much clear now. If you feel they're incomplete, feel free to edit them yourself. – Serg Derbst Dec 01 '15 at 08:06

1 Answers1

1

I figured it out. I knew it was something small and probably silly.

After tinkering with compile time weaving I've noticed that I annotated the wrong method. My service has two public methods, whereas one is calling the other (maybe not a good design after all), like this:

@Component
public class AnyService {

    public Object firstMethod() {
        //doing stuff...
        this.secondMethod();
    }

    @Prohibited
    public Object secondMethod() {
        //here be breakpoint
    }
}

Therefore I was already beyond the proxy when the breakpoint hit and since the first method wasn't annotated, the aspect was not executed.

Note to self: The method call must come from outside of the service or it will pass the proxy unnoticed.

I hope this will help someone, running into the same issue.

Serg Derbst
  • 444
  • 6
  • 18
  • 1
    Yes, this is the proxy self-invocation problem, as pointed out in the AOP docs: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies - If your `@Prohibited` method can be accessed through multiple code paths, your advice will not be applied correctly. You might want to rethink the design of that service. – Thorn G Nov 30 '15 at 16:49