javaspringspring-bootinversion-of-control

SpringApplication.run() without boot


I'm a total beginner with Spring. I'm currently trying to just run a very simple project just to start learning about Spring and understanding IoC and dependency injection. At first I created a simple Spring Boot project and it worked, but now I'm trying to do it without boot because I want to understand better what's going on. I don't understand how to do this (full class below):

SpringApplication.run(TestSpringApplication.class, args);

I tried to do this:

TestSpringApplication app = new TestSpringApplication();
app.run(args);

But then inside the run() method, the @Autowired messageRepository is null. I'm guessing this is because I created my TestSpringApplication using new instead of applicationContext.getBean()? But how can I access/get the applicationContext in my static main method?

Forgive me if it might sound trivial, I'm starting from almost zero here and I have a very hard time finding easy tutorials.

Here is what I'm trying to do but is not working:

package com.cypherf;
import com.cypherf.model.Message;
import com.cypherf.repository.MessageRepository;
import com.cypherf.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class TestSpringApplication {
    @Autowired
    private ApplicationContext context;

    @Autowired
    private MessageRepository messageRepository;

    @Autowired
    private MessageService messageService;

    public static void main(String[] args) {
        TestSpringApplication app = new TestSpringApplication();
        app.run(args);
    }

    public void run(String... args) {
        messageRepository.save(new Message("Message one"));
        messageRepository.save(new Message("Message two"));
        messageRepository.save(new Message("Message two"));

        messageService.printAllMessages();
    }
}

Here is my previous class with Spring Boot which is working:

package com.cypherf;
import com.cypherf.model.Message;
import com.cypherf.repository.MessageRepository;
import com.cypherf.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestSpringApplication implements CommandLineRunner {

    @Autowired
    private MessageRepository messageRepository;

    @Autowired
    private MessageService messageService;

    public static void main(String[] args) {
        SpringApplication.run(TestSpringApplication.class, args);
    }

    public void run(String... args) throws Exception {
        messageRepository.save(new Message("Message one"));
        messageRepository.save(new Message("Message two"));
        messageRepository.save(new Message("Message two"));

        messageService.printAllMessages();
    }
}

Solution

  • If you're not using Spring Boot, then you have to set up the Spring container by yourself by creating an ApplicationContext bean.

    One way of doing so is by using the AnnotationConfigApplicationContext class. For example:

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(TestSpringApplication.class);
        MessageService messageService = context.getBean(MessageService.class);
        messageService.printAllMessages();
    }
    

    But this only creates a basic application context for you. Without Spring Boot, you no longer have access to autoconfigurations. This means that:

    In addition, classes like CommandLineRunner are also no longer available. Now you have to rely on alternatives such as listening to the ContextRefreshedEvent.