I understand that a proxy bean is created when using AOP. I don't understand why I cannot access a property directly in my main method? Do we always have to use method to get a value when using AOP?
package power.sam;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import river.sam.Ganga;
import javax.inject.Inject;
import java.util.stream.Stream;
@Slf4j
@ComponentScan(basePackages = {"power.sam"})
@EnableAspectJAutoProxy
@Data
public class MyTest {
Employee emp;
@Autowired
MyTest(Employee emp){
this.emp = emp;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyTest.class);
context.registerShutdownHook();
MyTest bean = (MyTest) context.getBean("myTest");
log.info("Bean is {}", bean);
log.info("Bean.Employee via getter is {}", bean.getEmp());
log.info("Bean.Employee via property is {}", bean.emp);
}
}
output
14:51:12.925 [main] INFO power.sam.MyTest - Bean is MyTest(emp=power.sam.Employee@3700ec9c)
14:51:12.955 [main] INFO power.sam.MyTest - Bean.Employee via get is power.sam.Employee@3700ec9c
14:51:12.955 [main] INFO power.sam.MyTest - Bean.Employee via property is null
update : This seems to only happen if I have a all method advice like
@Before("execution(public * *(..))")
To get clarity on what is happening in the background , add the following to the test code.
System.out.println("Bean.Employee runtime class is "+ bean.getClass());
Complete main method will be as follows
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyTest.class);
context.registerShutdownHook();
MyTest bean = (MyTest) context.getBean("myTest");
System.out.println("Bean is "+ bean);
System.out.println("Bean.Employee via getter is "+ bean.getEmp());
System.out.println("Bean.Employee runtime class is "+ bean.getClass());
System.out.println("Bean.Employee via property is "+ bean.emp);
}
Now when run with a Pointcut expression as : @Before("execution(public * *(..))")
The console log is
Bean is rg.so.qn64919052.MyTest@662f5666
Bean.Employee via getter is rg.so.qn64919052.entity.Employee@75ed9710
Bean.Employee runtime class is class rg.so.qn64919052.MyTest$$EnhancerBySpringCGLIB$$6e53892a
Bean.Employee via property is null
Note : the bean.getClass()
returned the class of the Proxy object created.
To provide clarity, Employee
class is created within the package rg.so.qn64919052.entity
and now when run with a Pointcut expression as : @Before("execution(public * rg.so.qn64919052.entity.Employee.*(..))")
The console log is
Bean is rg.so.qn64919052.MyTest@acdb094
Bean.Employee via getter is rg.so.qn64919052.entity.Employee@674bd420
Bean.Employee runtime class is class rg.so.qn64919052.MyTest
Bean.Employee via property is rg.so.qn64919052.entity.Employee@674bd420
Note : the bean.getClass()
returned the original class.
Explanation :
Spring AOP is proxy based . Dynamic proxies only inherit methods and not any instance variables.
The pointcut expression in the first run had a global scope , which will advice the public (protected and package-scoped , since CGLIB) method executions of all Spring beans. For this reason , a proxy for MyTest got created .
The pointcut expression in the second run had a limited scope (rg.so.qn64919052.entity.Employee.*
) and MyTest was not proxied.
References :