Search code examples
javaspringaopaspectjspring-aop

AspectJ within(is(FinalType)) missed


I'm using aspectJ 1.8.10. In my code I have a bean with ScheduledExecutorService:

@Bean
public ScheduledExecutorService backgroundTaskExecutor() {
    return Executors.newSingleThreadScheduledExecutor();
}

When bean instantiated, proxy class throws:

.AopConfigException: Could not generate CGLIB subclass of class [class java.util.concurrent.Executors$DelegatedScheduledExecutorService]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: No visible constructors in class java.util.concurrent.Executors$DelegatedScheduledExecutorService

I know, that ScheduledExecutorService haven't constructor, is the root cause. But I need to configure my aspect's pointcut to exclude FinalType classes. Like this:

@Before("!within(is(FinalType)) && execution(* your_method_name(..)) ")

But, as I mentioned, aspectJ version 1.8.10 is not recognize is(..) syntax. (Intellij IDEA warning Cannot resolve symbol 'is'). Application starts without AOP issues, but fails with

java.lang.IllegalArgumentException: No visible constructors in class java.util.concurrent.Executors$DelegatedScheduledExecutorService

What I'm doing wrong? Is there any changes is aspectj > 1.8.4? (is(..) syntax)


Solution

  • You have Spring AOP configured to force creation of CGLIB proxies even for interface types like ScheduledExecutorService, probably via

    @EnableAspectJAutoProxy(proxyTargetClass = true)
    

    Just remove the proxyTargetClass = true part or set to false, then your aspect will work. You do not need any is(FinalType) pointcut designator, just write something like

    @Before("execution(* schedule*(..))")
    

    in order to intercept scheduler methods.


    Update: Let me explain why is(FinalType) does not help you and why thinking it does not work is wrong:

    Read the error messages again:

    Could not generate CGLIB subclass of class
      [class java.util.concurrent.Executors$DelegatedScheduledExecutorService]:
      Common causes of this problem include using a final class or a non-visible class;
      nested exception is
        java.lang.IllegalArgumentException: No visible constructors in class
        java.util.concurrent.Executors$DelegatedScheduledExecutorService
    

    "No visible constructors" does not mean the class is final, it means what it says: There just are no visible constructors. Actually the inner static class Executors.DelegatedScheduledExecutorService is package-protected in java.util.concurrent where Executors resides. If you look at the source code you see:

    static class DelegatedScheduledExecutorService
            extends DelegatedExecutorService
            implements ScheduledExecutorService {
    
        private final ScheduledExecutorService e;
    
        DelegatedScheduledExecutorService(ScheduledExecutorService executor) {
            super(executor);
            e = executor;
        }
    
        // (...)
    }
    

    See? No final class here. The actual problem is that CGLIB just cannot create a subclass due to JVM limitations: You cannot subclass something that is in another package if it is not public.

    This is why I told you to let Spring use a JDK dynamic proxy and take advantage of the fact that in this case subclassing is not necessary but implementing an interface is enough.