0

I have a demo class FooComponent which is autowired in FooService and accessed in its constructor.

FooComponent.class:

@Component("fooComponent")
public class FooComponent {
    public String format() {
        return "foo";
    }
}

FooService.class:

@Component
public class FooService {

    @Autowired
    private FooComponent fooComponent;

    public FooService() {
        System.out.println("in foo service const=" + this.fooComponent);
    }

    String doTatti() {
        return fooComponent.format();
    }
}

MainApplication:

@SpringBootApplication
public class InterviewApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(InterviewApplication.class, args);
        System.out.println(ctx.getBean(FooService.class).doTatti());
    }
}

In this tutorial, author says In the above example, Spring looks for and injects fooFormatter when FooService is created. In my case, fooFormatter is fooComponent.

But everytime, in the constructor my autowired property is null. My assumption is that it is because FoOService has not completely initialized? Is this correct?

If my assumption is correct, then why does below code work?

@Autowired
public FooService(FooComponent fooComponent) {
        System.out.println("in foo service const=" + fooComponent);
}

I know this is very basic and stupid question, but I need help in understanding it.

UPDATE:

One last query, is there a way to Autowire my FooService instance in MainApplication, instead of getting it from ApplicationContext?

Thank you in advance.

Community
  • 1
  • 1
cFrags
  • 11
  • 5

3 Answers3

1

In Spring, a bean is instantiated before its properties are injected. That is:

  1. Instantiate the bean first
  2. Inject the properties This is because spring uses setter methods of the instantiated bean in order to inject properties.

This is the reason you are getting NullPointerException.

Preferable and recommended option is to use constructor injection, not a property or setter injection. In additional, one of the ways to resolve this is to create constructor with parameter as you did.

The whole explanation you can check here: https://dzone.com/articles/spring-accessing-injected

EDIT: Beans should be managed by the container. If we want to use one of them, we should rely on dependency injection rather than a direct call to ApplicationContext.getBean().

See this post: Why is Spring's ApplicationContext.getBean considered bad?

Lemmy
  • 2,437
  • 1
  • 22
  • 30
0

You need to use constructor injection instead of setter injection. one of the drawbacks of setter injection is that it does not ensures dependency Injection. You can not guarantee that certain dependency is injected or not, which means you may have an object with incomplete dependency. On the other hand, constructor Injection does not allow you to construct an object until your dependencies are ready.

0
  1. for your first question, u r right. Also when u need an object in the constructor you have to use the constructor injection and not property injection with @autowired;
  2. for your second one, u can use the @Autowired prop in the main, but usually in this case is better to do your logic in the override of the commandLineRunner interface as follow :

    @Autowired
    private FooService foo;
    
    public static void main(String[] args) {
        SpringApplication.run(InterviewApplication.class, args);
    }
    
    @Override
    public void run(String... args) throws Exception {
    foo.doTatti();  
    ...
    }
    
solocoding
  • 81
  • 1
  • 3