springaopaspectjspring-aoppointcut

Creating A pointcut using args designator with no types results in BeanCurrentlyInCreationException


Using Spring AOP, when attempting to create a Pointcut using the designator args while not providing any types results in a series of exceptions starting with BeanCurrentlyInCreationException

The Example

object _001_Spring_AOP_Pointcut_Args_NoArgs {

    open class BeanA {
        open fun m() {
            println("BeanA#m()")
        }
    }

    @Aspect
    class AspectA {
        @Pointcut("args()")
        private fun pc_noArgs() = Unit

        @After("sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs.AspectA.pc_noArgs()")
        private fun ac_noArgs() = println("ac_noArgs")
    }

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    open class Config {
        @Bean
        open fun beanA(): BeanA = BeanA()

        @Bean
        open fun aspectA(): AspectA = AspectA()
    }

    fun runJava() {
        AnnotationConfigApplicationContext(Config::class.java)
    }

}

The Run Method

    @Test
    fun test_run() {
        _001_Spring_AOP_Pointcut_Args_NoArgs.runJava()
    }

The Exception Summary

Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanA' defined in sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs$Config: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs$BeanA]: Factory method 'beanA' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'aspectA' defined in sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs$Config: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs$AspectA]: Factory method 'aspectA' threw exception; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'aspectA': Requested bean is currently in creation: Is there an unresolvable circular reference?


Solution

  • args() vs. execution(* *())

    This is an unusual way of intercepting all methods without parameters. I would rather make it more explicit and use execution(* *()) instead. If you ever migrate from Spring AOP to AspectJ, you will notice that args() matches all kinds of pointcuts there, e.g. call, initialization, preinitialization, staticinitialization, get and constructor calls/executions without parameters.

    Pointcut scoping

    Here in Spring AOP your problem might be another one, though: Your pointcut is too broad. It matches many joinpoints in Spring beans, also in Spring or third-party classes themselves. So you want to limit the scope of your pointcut, e.g. something like

    execution(* *()) && within(my.own.package.**)
    

    When to (not) use fully qualified class names in pointcuts

    BTW, if your pointcut is defined in the very same class as the advice using it, it should not be necessary to use a fully qualified class name, i.e. instead of

    @After("sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs.AspectA.pc_noArgs()")
    

    you could use

    @After("pc_noArgs()")
    

    Inline pointcuts

    If you have no plans to re-use the same pointcut in other advices, just get rid of @Pointcut and define an inline pointcut such as

    @After("execution(* *()) && within(my.own.package.**)")
    

    Advices should be public

    While it is OK to define the @Pointcut method as private if you do not refer to it from other classes, the @After advice should be public. It might work in Spring AOP, but is against conventions. Again, if you ever migrate to AspectJ, you will even get a compile error because an AspectJ advice must be public.