Search code examples
javaspringaop

Spring aop breaks original code? The field of the autowired bean becomes null when the aspect is enabled


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). 

Solution

  • 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

    1. 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.

    2. 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

    spring singleton bean fields are not populated

    Spring AOP CGLIB proxy's field is null