5

I am trying to inject an object to a class. However, the field is always null. I tried @Autowired and @Resource annotations. I am not creating the object with new operator anywhere. The constructor of Foo is called properly.

The minimal example of this problem:

Foo class

package foo.bar;
public class Foo {
    Foo(){
        System.out.println("Foo constructor");
    }
    public void func() {
        System.out.println("func()");
    }
}

Bar class

package foo.bar;
public class Bar {
    @Autowired
    private Foo foo;

    public Bar() {
        foo.func();
    }
}

Entry point

package foo.bar;
public class HelloApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    }
}

spring-config.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:component-scan base-package="foo.bar"/>
    <bean id = "foo" class="foo.bar.Foo" />
    <bean id = "bar" class="foo.bar.Bar" />
</beans>

Why is foo field of Bar class always null? How can I fix this?

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
RK1
  • 429
  • 2
  • 7
  • 28
  • 3
    Constructors are called before Fields are autowired. Therefore calling foo.func(); in the Bar constructor will result in an NullPointer. You can create a method to call foo.func(); and annotate it with @PostConstruct to get around this – Michael W Mar 07 '16 at 15:06

1 Answers1

2

As @Mick pointed out, field injection necessarily takes place after the constructor is finished (there's no other way for Spring to see the instance and manipulate it). Modify your class to use constructor injection, and you'll both make your dependencies more explicit (and thus easier to test, for example) and eliminate what's essentially a race condition:

public class Bar {
    private Foo foo;

    @Autowired
    public Bar(Foo foo) {
        this.foo = foo;
        foo.func();
    }
}
chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • Thank you, that works just fine. I just thought that the whole initialization in Spring is (or should be) always being performed in the xml file when declaring beans. Now I will have to create the Foo object somewhere and pass it to the constructor. – RK1 Mar 07 '16 at 15:21
  • @RK1 Spring knows how to autowire constructors. You shouldn't need to change your XML bean definitions at all. (You can *explicitly* supply constructor arguments in XML if you want, but as long as the beans are unambiguous, Spring can resolve them for you.) – chrylis -cautiouslyoptimistic- Mar 07 '16 at 15:23
  • Yes, I don't have to change anything. I just thought you don't need to instantiate beans anywhere when using Spring. – RK1 Mar 07 '16 at 15:24
  • 1
    @RK1 You won't, usually, at least by hand, but *something* has to instantiate the beans, or where would they come from? – chrylis -cautiouslyoptimistic- Mar 07 '16 at 15:26
  • What if `Bar` is not a bean? We would still have to explicitly create a `Foo` object and pass it to `Bar` constructor and hence @Autowired becomes useless, doesn't it – mangusta Dec 14 '19 at 18:09
  • And besides, how soon does the autowiring take place? For instance, I have created an instance of class `A` having autowired field inside, and checked that field for being null, and it was still null, even though the constructor of `A` had finished executing – mangusta Dec 14 '19 at 18:31