When the aspect is enabled, the @Autowired bean in BeanB
becomes a proxy, and the name
field is null. Why? What should I do if I wish the original code to work properly?
Here is the code:
public class BeanA
{
@Value("jami")
//public String name;
String name; //package visiblity
}
public class BeanB
{
@Autowired
private BeanA beanA;
public void noLongerWorks()
{
System.out.println(beanA.name);
}
}
public class Main
{
public static void main(String[] args)
{
String[] configs = {"applicationContext.xml", "applicationContext-aop.xml"};//prints null
// String[] configs = {"applicationContext.xml"};//prints jami
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);
BeanB beanB = ctx.getBean(BeanB.class);
beanB.noLongerWorks();
}
}
---------- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<bean class="aop.pack1.BeanA" />
<bean class="aop.pack1.BeanB" />
</beans>
------ applicationContext-aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
<bean class="aop.pack1.TestAspect" />
</beans>
@Aspect
public class TestAspect
{
@Pointcut("target(aop.pack1.BeanA)")
public void pointcut() {}
@Before("pointcut()")
public void advice()
{
System.err.println("___________advice__________");
}
}
EDIT: I figured out one possible solution. But it does not seems very clean. Is there any elegant way to this? without making changes to existing code? The solution I found:
is to make all the fields in BeanA private, and only access them via getter setters.
This approach, however, requires a lot of modification of the original code (e.g. the BeanA class).
You have already figured out the issue but, I wanted to share this article I came across that lists what Spring AOP
can and cannot do.
In your case
Since it uses proxy-based AOP, only method-level advising is supported; it does not support field-level interception So join-points can be at method level not at field level in a class.
Only methods with public visibility will be advised: Methods with private, protected, or default visibility will not be advised.
Just a recommendation and I think it's also a good OOP practice to create fields with private or protected
visibility and provide appropriate getters and setters to access them.
These SO Q/A might be useful
Spring AOP - get old field value before calling the setter