Master ApplicationRunner and CommandLineRunner: Usage and Implementation in Spring Boot

This article explains the purpose of Spring Boot's ApplicationRunner and CommandLineRunner interfaces, demonstrates common startup tasks such as data initialization, configuration loading, cache population and license verification, compares their parameter handling, shows how to control execution order with @Order, and reveals the internal call chain that Spring uses to invoke these runners.

Shepherd Advanced Notes
Shepherd Advanced Notes
Shepherd Advanced Notes
Master ApplicationRunner and CommandLineRunner: Usage and Implementation in Spring Boot

Overview

ApplicationRunner and CommandLineRunner are two Spring Boot extension points that allow developers to execute custom logic after the application context has been fully initialized. They are typically used for one‑time startup tasks such as loading initial data, validating configuration, or performing scheduled work.

Typical Use Cases

Data initialization – a DataInitializer bean implements CommandLineRunner (or ApplicationRunner) to insert required records into the database, create a super‑admin account, and set a flag to avoid repeated inserts on subsequent restarts.

@Component
public class DataInitializer implements CommandLineRunner {
    @Resource
    private EnvInitMapper envInitMapper;
    @Resource
    private UserService userService;
    @Resource
    private RoleService roleService;
    @Resource
    private UserRoleService userRoleService;

    @Override
    public void run(String... args) throws Exception {
        // 1. Check if this is the first start
        QueryWrapper<EnvInit> queryWrapper = new QueryWrapper<>();
        EnvInit init = envInitMapper.selectOne(queryWrapper);
        if (Objects.isNull(init)) {
            // 2. First‑time initialization
            userService.firstInitData();
            // 3. Insert initialization flag
            init = new EnvInit();
            init.setIsInit(1);
            envInitMapper.insert(init);
        }
    }

    /** Initialize all required baseline data */
    @Transactional(rollbackFor = Exception.class)
    public void initData() {
        userService.firstInitData();
        roleService.firstInitData();
        userRoleService.firstInitData();
    }
}

Configuration loading – a ConfigInitializer bean implements CommandLineRunner to read external configuration files or database parameters and cache them.

@Component
public class ConfigInitializer implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("Loading configuration...");
        // Config config = configService.loadConfig();
    }
}

Cache population – a DataCacheInitializer bean implements ApplicationRunner to load dictionary data into an in‑memory cache.

@Component
public class DataCacheInitializer implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        System.out.println("Loading dictionary data into cache...");
        // List<Dict> dicts = dictService.loadAll();
        // cacheService.cacheDicts(dicts);
    }
}

License verification – a LicenseCheckApplicationRunner bean uses ApplicationRunner to install and validate a license file, checking system information such as CPU ID or UUID.

@Component
public class LicenseCheckApplicationRunner implements ApplicationRunner {
    @Resource
    private LicenseVerify licenseVerify;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        LicenseContent content = licenseVerify.install();
    }
}

@Component
public class LicenseVerify {
    @Resource
    private LicenseProperties licenseProperties;
    private static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public synchronized LicenseContent install() {
        LicenseContent result = null;
        try {
            LicenseManager licenseManager = new LicenseManager(initLicenseParam());
            licenseManager.uninstall();
            result = licenseManager.install(new File(licenseProperties.getLicensePath()));
            verifySystemInfo(result);
            logger.info("License installed, valid period: {} - {}", df.format(result.getNotBefore()), df.format(result.getNotAfter()));
        } catch (Exception e) {
            logger.error("License installation failed:", e);
            throw new BizException("License installation failed");
        }
        return result;
    }
    // ... other verification methods omitted for brevity ...
}

Controlling Execution Order

When multiple runners exist in the same application, the @Order annotation can be used to define their execution sequence. A lower order value runs first.

@Component
@Order(1)
public class FirstRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("FirstRunner running!");
    }
}

@Component
@Order(2)
public class SecondRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("SecondRunner running!");
    }
}

During startup, FirstRunner executes before SecondRunner. The same approach works for ApplicationRunner implementations.

Differences Between the Two Interfaces

Parameter handling : CommandLineRunner receives a raw String... args array, while ApplicationRunner receives an ApplicationArguments object that separates option and non‑option arguments and provides convenient accessor methods.

Typical scenarios : Use CommandLineRunner for simple command‑line processing or quick tasks; use ApplicationRunner when you need richer argument handling.

Parameter management : ApplicationArguments lets you query option names, option values, and non‑option arguments, which CommandLineRunner cannot do directly.

Implementation Details

The core of Spring Boot’s runner execution lives in SpringApplication.run(). After the application context is refreshed, Spring calls #callRunners(context, applicationArguments), which collects all beans of type ApplicationRunner and CommandLineRunner, sorts them using AnnotationAwareOrderComparator, and invokes each via #callRunner().

public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    // ... omitted setup code ...
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // ... create context, refresh, etc. ...
    callRunners(context, applicationArguments);
    // ... handle failures, log startup info ...
    return context;
}

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
    try {
        runner.run(args);
    } catch (Exception ex) {
        throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
    }
}

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
    try {
        runner.run(args.getSourceArgs());
    } catch (Exception ex) {
        throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
    }
}

Conclusion

Both CommandLineRunner and ApplicationRunner are useful for executing logic after a Spring Boot application starts. Choose CommandLineRunner for straightforward tasks that only need the raw argument array, and ApplicationRunner when you need richer argument parsing. Use the @Order annotation to guarantee a deterministic execution sequence for multiple runners.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaSpring BootInitializationApplicationRunnerRunnerCommandLineRunner
Shepherd Advanced Notes
Written by

Shepherd Advanced Notes

Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.