Java aop ComponentScan not working & AnnotationConfigApplicationContext getBean not working

后端 未结 3 1784
一个人的身影
一个人的身影 2021-01-14 10:35

I wrote a simple set of classes to show a friend about using Annotations for AOP (instead of xml config) . We couldnt get the @ComponentScan to work AND AnnotationConfigAppl

3条回答
  •  逝去的感伤
    2021-01-14 11:31

    A1 , @ComponentScan didn't work because it is commented out from the "The component classes to use for loading an ApplicationContext." or PersonOperationsConfigClass

    @Configuration
    //@ComponentScan(basePackages = {"samples.chapter3"})
    @EnableAspectJAutoProxy
    
    public class PersonOperationsConfigClass {}
    

    The test class gets the ApplicationContext created from the component classes specified with the @ContextConfiguration annotation. Since no components were created or auto detected , @Autowired failed.

    When AnnotationConfigApplicationContext was used within a method annotated with @Before , an ApplicationContext was programmatically created.ctx.scan("samples.chapter3"); scanned and auto-deteced PersonOperations annotated with @Component. obj reference got set with the code obj = ctx.getBean(PersonOperationsI.class);. This object was not 'Autowired'.

    Update based on the comment from OP

    The Junit 4 annotations and the @ExtendWith(SpringExtension.class) combination is not working for me.

    Following Test class runs successfully with zero errors/failures. obj is autowired and not null. I have used the corresponding annotations from Junit 5.

    package rg.app.aop.so.q1;
    
    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit.jupiter.SpringExtension;
    
    @ExtendWith(SpringExtension.class)
    @ContextConfiguration(classes= {PersonOperationsConfigClass.class})
    public class PersonOperationsTest {
    
        @Autowired
        private PersonOperationsI obj;
    
        @BeforeEach
        public void setUp() {
            System.out.println("init ::"+ obj);
            Assertions.assertNotNull(obj);
        }
    
        @Test
        public void testPersonOps() {
            Assertions.assertNotNull(obj);
        }
    }
    

    Configuration class

    package rg.app.aop.so.q1;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configuration
    @EnableAspectJAutoProxy
    @ComponentScan(basePackages = {"rg.app.aop.so.q1"})
    public class PersonOperationsConfigClass {
    
    }
    

    A2, Following are my analysis.

    Remember , @EnableAspectJAutoProxy has got a default value "false" for proxyTargetClass attribute. This attribute determines the proxying mechanism : JDK proxy (false) or CGLIB proxy (true) .

    Here the presence of a valid Aspect with a valid advice results in the actual proxying to kick in. A component will get proxied only when the advice has any effect on it. In short , the proxying of a Component happens only if required.

    Case 1

    When : @EnableAspectJAutoProxy / @EnableAspectJAutoProxy(proxyTargetClass = false )

    • ctx.getBean(InterfaceType) returns a bean
    • ctx.getBean(ImplementationClassType) fails to return a bean

    Case 2

    When : @EnableAspectJAutoProxy(proxyTargetClass = true )

    • ctx.getBean(InterfaceType) returns a bean
    • ctx.getBean(ImplementationClassType) returns a bean

    Case 3

    When : @EnableAspectJAutoProxy annotation is absent

    • ctx.getBean(InterfaceType) returns a bean
    • ctx.getBean(ImplementationClassType) returns a bean

    Case 1 , Spring AOP is enabled with proxyTargetClass as false. JDK proxy creates a proxy bean of Interface type. The bean created is of type InterfaceType and not ImplementationClassType . This explains why ctx.getBean(ImplementationClassType) fails to return a bean.

    Case 2 , Spring AOP is enabled with proxyTargetClass as true . CGLIB creates a proxy bean by subclassing the class annotated with @Component. The bean created is of type ImplementationClassType , as well qualifies as InterfaceType. So both the getBean() calls returns this bean successfully.

    Case 3,

    Spring only create "proxy" objects if any special processing is required ( eg: AOP , Transaction Management ).

    Now with this logic , since @EnableAspectJAutoProxy is absent , a bean gets created for class annotated with @Component without any proxying. The bean created is of type ImplementationClassType , as well qualifies as InterfaceType. So both the getBean() calls returns this bean successfully.

    Analysis done with the following code.

    package rg.app.aop.so.q1;
    
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class AppMain {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
            ctx.scan("rg.app.aop.so.q1");
            ctx.refresh();
            System.out.println();
            for(String name:ctx.getBeanNamesForType(PersonOperationsI.class)) {
                System.out.println(name);
            }
            for(String name:ctx.getBeanNamesForType(PersonOperations.class)) {
                System.out.println(name);
            }
            PersonOperationsI obj = ctx.getBean(PersonOperationsI.class);
            System.out.println(obj.getClass());
    
            obj = ctx.getBean(PersonOperations.class);
            System.out.println(obj.getClass());
            ctx.registerShutdownHook();
    
        }
    
    }
    

    Case 1 prints

    personOperations
    class com.sun.proxy.$Proxy18
    Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'rg.app.aop.so.q1.PersonOperations' available
    

    Case 2 prints

    personOperations
    personOperations
    class rg.app.aop.so.q1.PersonOperations$$EnhancerBySpringCGLIB$$c179e7f2
    class rg.app.aop.so.q1.PersonOperations$$EnhancerBySpringCGLIB$$c179e7f2
    

    Case 3 prints

    personOperations
    personOperations
    class rg.app.aop.so.q1.PersonOperations
    class rg.app.aop.so.q1.PersonOperations
    

    Hope this helps

提交回复
热议问题