0

I'm playing around with spring-boot auto wiring to learn more about it and I'm not understanding something. In the code below the autowiring for the startup class works, but the one in the highier level class does not. The result is the "SpringApplication.run" will print out the properties correctly, but "demo.print()" will not. Can someone help me understand why and how I would get it to work?

package demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;

@SpringBootApplication
@EnableConfigurationProperties(EmailProperties.class)
public class DemoApplication {


@Autowired
private EmailProperties properties;

public static void main(String[] args) {

    SpringApplication.run(DemoApplication.class, args);
    
    DemoApplication demo = new DemoApplication();
    demo.print();
    
}

private void print() {
    System.out.println("Partner support: " + this.properties.getPartnerSupport());
}


DemoApplication(){
}


@Service
static class Startup implements CommandLineRunner {

    @Autowired
    private EmailProperties properties;

    @Override
    public void run(String... strings) throws Exception {
        System.out.println("-----------------------------------------");
        System.out.println("Client support: " + this.properties.getClientSupport());
        System.out.println("Partner support: " + this.properties.getPartnerSupport());
        System.out.println("No reply to: " + this.properties.getNoReplyTo());
        System.out.println("Support reply to: " + this.properties.getSupportReplyTo());
        System.out.println("OpS notification: " + this.properties.getOpsNotification());
        System.out.println("-----------------------------------------");
    }
}

}

I can post the other parts of the demo code if needed. Thanks.

Travis Frazier
  • 441
  • 3
  • 13

1 Answers1

2

When you invoke SpringApplication.run(DemoApplication.class, args);, Spring will create its own instance of DemoApplication, see that it has @Autowired members and autowire them. This is what's usually called a managed bean.

Your @Service class will also be instantiated and autowired automatically, and because it's also a CommandLineRunner, Spring will call the run() method after having autowired your beans.

On the other hand, when you invoke DemoApplication demo = new DemoApplication();, you create your own instance of DemoApplication, which is not managed by Spring. As a result, nothing will be autowired in the properties field, which stays null.

To better understand how Spring works, especially the managed vs non-managed instances part, I highly suggest you read the first chapter of the Spring Framework Reference, The IoC Container.

Bastien Jansen
  • 8,756
  • 2
  • 35
  • 53
  • So the SpringBootApplication tag isn't enough to have a class managed by spring? How would you wire a class that wasn't using Service for something like a WebServlet – Travis Frazier Sep 15 '20 at 20:20
  • It's enough to make Spring manage its own instance, but in your example you are creating a separate instance yourself, and this one will not be managed by Spring. If you want Spring to create an instance of a class that is not annotated with `@Service` and such, please refer to https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-java-basic-concepts – Bastien Jansen Sep 17 '20 at 14:20