The Main Application Class

What @SpringBootApplication switches on, what SpringApplication.run does, and why the main class should stay almost empty.

Syntax@SpringBootApplication(exclude = SomeAutoConfiguration.class) public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }

Every Boot application has one class with a main method that calls SpringApplication.run. That call creates the ApplicationContext, loads configuration, applies auto-configuration, creates every bean, starts the embedded server and returns the running context.

Three annotations in one

@SpringBootApplication combines:

  • @SpringBootConfiguration: marks the class as the application's main configuration. Test annotations search upwards for it.
  • @EnableAutoConfiguration: applies Boot's auto-configuration based on the classpath and your beans.
  • @ComponentScan: finds @Component, @Service, @RestController and friends in this package and below.

Its attributes tune those parts: exclude switches off specific auto-configurations, and scanBasePackages widens scanning. Needing scanBasePackages usually means the main class is in the wrong package.

Keep it nearly empty

The main class is also what test slices load. @WebMvcTest finds it and applies everything declared on it. Put @EnableScheduling, @EnableJpaRepositories or a set of @Bean methods here, and every slice test inherits them: a web test suddenly wants a database. Move such things into separate @Configuration classes in the relevant feature package, where slices leave them out.

Running code at start-up

Do not put start-up logic in main after run, and avoid long work in constructors. Implement ApplicationRunner (or CommandLineRunner) as a bean: it runs once the context has started, before the application is marked ready, and an exception from it stops start-up with a clear error. For work that should happen after everything is up, listen for ApplicationReadyEvent.

Runners block start-up while they run. A slow one keeps the readiness state at refusing traffic, so in Kubernetes the pod receives no requests and a rollout can time out waiting for it. Long jobs belong in a scheduled or asynchronous task.

Customising SpringApplication

Instead of the static run, you can create a SpringApplication and set options such as the banner mode, lazy initialisation or extra profiles before starting it. Most of these also exist as spring.main.* properties, which are easier to change per environment and should be preferred.

Example

Example · java

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class OrdersApplication {

    public static void main(String[] args) {
        var app = new SpringApplication(OrdersApplication.class);
        app.setBannerMode(Banner.Mode.OFF);
        ConfigurableApplicationContext context = app.run(args);

        // The returned context is live: the server is listening at this point.
        System.out.println("Beans created: " + context.getBeanDefinitionCount());
    }
}

When to use it

  • Fail start-up early when a required external file or table is missing, using an ApplicationRunner.
  • Stop a @WebMvcTest from needing a database by moving @EnableJpaRepositories off the main class.
  • Run a service that has a JDBC library on its classpath but deliberately no DataSource.

More examples

Start-up work with an ApplicationRunner

ApplicationArguments parses --name=value options for you, unlike CommandLineRunner's raw String array. If run throws, the application fails to start and the exception is in the log, which is the right outcome for a check the service cannot run without. Keep the work short: start-up waits for it.

Example · java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class CatalogueCheck implements ApplicationRunner {

    private static final Logger log = LoggerFactory.getLogger(CatalogueCheck.class);

    @Override
    public void run(ApplicationArguments args) {
        // --dry-run on the command line shows up as an option name here.
        if (args.containsOption("dry-run")) {
            log.info("Dry run: skipping catalogue check");
            return;
        }
        log.info("Catalogue check passed");
    }
}

Reacting once the application is ready

ApplicationReadyEvent is published after all runners have finished, so it is the point where the application is actually serving. Logging the start-up time here gives you a number to watch across releases; a jump usually means a new starter or a slow bean.

Example · java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class ReadyLogger {

    private static final Logger log = LoggerFactory.getLogger(ReadyLogger.class);

    @EventListener(ApplicationReadyEvent.class)
    public void onReady(ApplicationReadyEvent event) {
        log.info("Ready after {} ms", event.getTimeTaken().toMillis());
    }
}

Switching off one auto-configuration

In Boot 4 DataSourceAutoConfiguration lives in org.springframework.boot.jdbc.autoconfigure, not the Boot 3 package. Excluding by class fails to compile if the class is missing, which is a useful safety net; the spring.autoconfigure.exclude property does the same from configuration, for example only in one profile.

Example · java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;

// The JDBC starter is on the classpath for a library, but this service has no database.
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class ReportingApplication {

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

Discussion

  • Be the first to comment on this lesson.