Faster Spring Boot startup

As software developers, we always look for opportunities to improve our efficiency at work and optimize repeatable activities. One of them is application startup. Even if you cover your production code with unit tests and follow TDD, from time to time checking how the whole application works is inevitable. The more often you run it, the more time is wasted on waiting until the application is ready to operate.

Although the fast restart provided by Spring Boot DevTools is helpful for library class loading, it doesn’t solve the issue with the long startup of your own application code. From this post you will learn how to decrease the total number of coffee breaks in daily work by configuring faster Spring Boot startup in your local development environment.

Advertisement

1. Why startup is slow

A brand new empty Spring Boot web application starts blazingly fast, no matter which embedded container you choose. Along with new features and dependencies, the application gets heavier and the startup time increases. Establishing connection to a database, fetching data from an integration endpoint over the network, or loading and parsing a 50 megabytes XML file from local resources, your application is extremely busy right after it is run, sometime for over a dozen minutes or more.

The reason why all the heavy lifting is done at the beginning is because we care about our users and we don’t want them to wait or experience bad performance. All resources are prepared and cached, so they are available right away when really requested.

On the other hand, as developers, we also don’t like to wait and waste time on unproductive examination of boot logs. All startup improvements for our users stand against us when our application is run locally and we don’t really need them. Fortunately, there is a simple solution that can be applied to any project to alleviate some of that pain and improve your development experience.

2. Sample issue

You probably know what takes plenty of time when your application is started, but for the demonstration purpose, we are going to use an example web project that can be found in the GitHub repository.

Imagine you have a Spring bean which loads and caches values from some resource. The source might be a remote web service or an array of local CSV files that need to be mapped into model objects. In production environment such activity is desired at startup.

However, if you run the application locally most likely you don’t always need this resource, but only when you play around with a feature that depends on it. In the sample below, we use the sleep() method to artificially extend startup of our sample application.

@Component
class ResourceLoader {

    private List<Term> resources;

    @PostConstruct
    void initHeavyLoading() throws InterruptedException {
        log.info("Loading start");
        // time-consuming execution
        Thread.sleep(10_000);
        resources = loadResources();
        log.info("Loading end");
    }
    
    // …

}

3. Nothing new

Spring Boot facilitates project setup with most common defaults and ready to use configurations, which is undeniably great because it saves us precious time. Yet, for newcomers to the framework it might hide some of the old features which are present in Spring for many years.

You probably know that @SpringBootApplication actually does nothing itself. It’s just a wrapper annotation for @Configuration, @EnableAutoConfiguration, and @ComponentScan with their defaults. However, from my experience not everyone knows about one particularly useful property of the @ComponentScan annotation which is lazyInit.

LazyInit is a boolean flag that indicates whether all discovered beans should be created by the container eagerly when it starts or when they are accessed for the first time. By default the flag is set to false, which is good in production but not necessarily when you develop the application on your local machine.

4. Getting lazy

What we want to achieve is to enable the bean lazy loading only in your local development environment and leave eager initialization for production. They say you can’t have your cake and eat it too, but with Spring you actually can.  All thanks to profiles.

@SpringBootApplication
public class LazyApplication {

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

    @Configuration
    @Profile("local")
    @ComponentScan(lazyInit = true)
    static class LocalConfig {
    }

}

The sample above presents an ordinary Spring Boot main class that you usually create as a first one in your application. As an addition, it contains a static inner configuration class with the lazy initialization set to true. The class is annotated with @Profile so that it is activated only if the local profile is enabled.

The principle of operation is simple. If the local profile is activated, the lazyInit in @ComponentScan on the LocalConfig class overrides the default value provided by the @SpringBootApplication annotation on the main class. If the local profile is deactivated, the LocalConfig class is ignored and the defaults are used.

Finally, you need to activate the local profile on your machine. There are a few options to choose from. The easiest one requires setting the spring.profiles.active environment variable either permanently in your OS or automatically by your favorite IDE before every application start. In IntelliJ, it can be specified in the Run/Debug configuration for the main class of the project.

Alternatively, if you have a separate Maven profile for local development and you use the Spring Boot Maven plugin to run your app, you can configure active profiles in your pom.xml.

You can test the functionality on the sample project in the GitHub repository. First, run the application without the local profile to see that the initialization of the ResourceLoader class is executed. Then set the profile and restart the application. The difference in the startup time should bring a smile on your face similar to the one below.

5. Disabling lazy initialization for selected beans

Although @ComponentScan(lazyInit = true) affects all scanned beans, you may override this behavior just for chosen components and initialize them eagerly. The @Lazy annotation has several different uses and can be applied complementary to the described scenario or or an alternative solution. If you’re interested in improving the startup speed of your Spring application, you should definitely familiarize yourself with the @Lazy annotation.

6. Learn your tool set

Before you reach for commercial runtime byte code manipulation tools like JRebel, it is worth to verify if the simple lazy initialization isn’t enough for working comfort in your development environment. Spring is a highly elaborated framework and it’s still growing. Not surprisingly, not knowing about some helpful feature isn’t a rare situation.

Although some people may argue that familiarization with the internals of a framework contradicts with the idea of using frameworks in general, this post shows that learning at least about the basics might be beneficial. The @ComponentScan is an example of what used to be a public API in Spring is now hidden by Spring Boot. This is the kind of internals that are worth to know about. If you have some questions or know any other useful trick, please share your idea in a comment.

Facebooktwittergoogle_plusredditlinkedinmail
Advertisement

4 Replies to “Faster Spring Boot startup”

Leave a Reply