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
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 beanctx.getBean(ImplementationClassType)
fails to return a beanCase 2
When : @EnableAspectJAutoProxy(proxyTargetClass = true )
ctx.getBean(InterfaceType)
returns a beanctx.getBean(ImplementationClassType)
returns a beanCase 3
When : @EnableAspectJAutoProxy
annotation is absent
ctx.getBean(InterfaceType)
returns a beanctx.getBean(ImplementationClassType)
returns a beanCase 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