'Why xml autowire injection failed when getter method return a type different from the property?

I wrote a snippet code to test autowire xml config. But I was getting the null pointer exception all the time which indicating that the field autowiring failed.

Contray to some other examples which can work, I found that the getter method return a type different from the field that is to be wired. Remove this getter method or modify the return type, then it works. But I don't know the reason. Why the getter method matters? I thought the setter method is used to wire, what about getter method ?

public class Department {

    private String departName;

    public String getDepartName() {
        return departName;
    }

    public void setDepartName(String departName) {
        this.departName = departName;
    }

}

public class Employee {
    private Department department;

//  1) below code will cause NPE
    public String getDepartment() {
        return department.getDepartName();
    }

//   2)below code works,  or just delete  1) code  works as well
    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    public void foo() {
        System.out.println(department.getDepartName());
    }
}

autowire xml config file looks like below:

<bean id="depart" class="com.kingdom.Department">
        <property name="departName" value="risk"/>
    </bean>
    <bean id="employee" class="com.kingdom.Employee" autowire="byType">
    </bean>

and finally the main class:

ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        Employee employee = (Employee) ctx.getBean("employee");
        employee.foo();

I expect the department can be autowired into the employee. But I got NPE.

java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run (ExecJavaMojo.java:294)
    at java.lang.Thread.run (Thread.java:748)
Caused by: java.lang.NullPointerException
    at com.kingdom.Employee.foo (Employee.java:17)
    at com.kingdom.Main.main (Main.java:14)
    at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:498)
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run (ExecJavaMojo.java:294)
    at java.lang.Thread.run (Thread.java:748)

After modifying the return type , it works.

But is there anyone gotta know what's going on behind? why getter method undermine the autowiring ?



Solution 1:[1]

To use autowire , you have to enable it by including <context:annotation-config> in beans.xml. You can also enable it by including <context:component-scan> , which will implicitly enables the functionality of <context:annotation-config>.

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

      <context:annotation-config>

      <bean> .... </bean>

</beans>

Then ,you have to annotate @Autowired on the Employee 's department setter to tell Spring to auto inject Department bean into the Employee bean. You can also annotate it on the department field or constructor.

public class Employee {

    @Autowired
    public void setDepartment(Department department) {
        this.department = department;
    }
}

Since Spring 4.3, if the target bean only have one constructor, @Autowired is no longer need to be explicitly annotated on the target bean. However, if several constructors are available , @Autowired must be exist to teach the container how to inject the bean.

So , that mean if you do not want to annotate any @Autowired on the Employee , you have to make Employee only has one constructor :

public class Employee {

    public Employee(Department department) {
        this.department = department;
    }
}

In both cases , <context:annotation-config> is still required to make the auto wiring to take effect.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1