Understanding Hot Deployment and Hot Reloading in Spring Boot | by Dwen | Aug, 2022

Sprint Boot hot deployment and hot reloading

Photo by Milk-Tea on Unsplash

In Spring Boot development and debugging, if we need to restart and debug each line of code modification, it may be time-consuming.

The Spring Boot team provides the sprint-boot-devtools (Short name: Devtools) plugin for this problem, which tries to improve the efficiency of development and debugging.

What are hot deployment and hot reloading?

Hot deployment and hot reloading can automatically update (reload or replace classes, etc.) the application while the application is running.

Note: The solution provided by sprint-boot-devtools also needs to be restarted, but it can be automatically loaded without a manual restart.

Strictly speaking, we need to distinguish between hot deployment and hot loading. For Java projects:

1. Hot deployment

  • Redeploy the project while the server is running.
  • It directly reloads the entire application, frees up memory, is cleaner and more thorough than hot reloading, and takes more time.

2. Hot loading

  • Reloads the class at runtime to upgrade the application.
  • The implementation principle of hot loading mainly depends on the class loading mechanism of java. The implementation method can be summarized as starting a background thread when the container starts and regularly detecting the change of the timestamp of the class file. If the timestamp of the class changes, the Class reloads.
  • Compared with the reflection mechanism, reflection obtains class information at runtime and changes program behavior through dynamic calls; hot loading is changing class information at runtime by reloading and directly changing program behavior.

LiveLoad is a tool that provides automatic loading and updating of browser clients. It is divided into two parts: LiveLoad Server and LiveLoad Browser plug-in.

The LiveLoad server has been integrated with devtoolsso if we are developing a web application and expect the browser to refresh automatically, we can consider LiveLoad now.

Only one LiveReload server can be running at the same time.

Before starting the application, make sure that no other LiveReload servers are running.

If multiple applications are launched from the IDE, only the first application will support LiveReload.

Configure devtools for hot deployment

1. POM configuration

Add the dependency of sprint-boot-devtools.

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- Can prevent passing devtools dependencies into other modules -->
</dependency>
</dependencies>

2. IDEA configuration

If you use IDEA development tools, there are usually two ways:

Method 1: When there is no configuration, manually trigger the restart update (Ctrl+F9).

You can also use mvn compile to trigger a restart update.

Method 2: IDEA needs to enable runtime compilation and automatically restart and update.

First select File -> Setting -> Build,Execution,Deployment -> Compile .

Then, check the Make project automatically.

Shortcut key: ctrl + alt + shift + / Select Registry check the compiler.automake.allow.when.app.running .

The new version of IDEA can be set as the first in File -> Setting -> Advanced Settings .

application.yml configuration

spring:
devtools:
restart:
enabled: true # Set to enable hot deployment
additional-paths: src/main/java # restart directory
exclude: WEB-INF/**
thymeleaf:
cache: false # Use Thymeleaf template engine, turn off caching

Using LiveLoad

The sprint-boot-devtools module contains an embedded LiveReload server that can be used to trigger browser refreshes when resources change.

The LiveReload browser extension supports Chrome, Firefox, and Safari, and you can download it for free from https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei.

Or download from the browser plugin center, such as Firefox:

After installation, it can be managed through the following icons.

You can set the spring.devtools.livereload property to false if you do not want to start the LiveReload server while the application is running.

Only one LiveReload server can be running at a time. Please make sure that no other LiveReload servers are running before starting the application.

If multiple applications are launched from the IDE, only the first application will support LiveReload.

sprint-boot-devtools uses two class loaders: one ClassLoader loads classes that will not change (third-party jar packages), and the other ClassLoader (restart ClassLoader) loads classes that will change (custom classes).

A file monitoring thread is started in the background. When the files in the monitored directory change, the original Restart ClassLoader is discarded, and the new Restart ClassLoader will be reloaded.

Because the third-party jar package is not reloaded after the file is changed, only the custom classes are loaded, and the loaded classes are few, so the restart is relatively faster.

This is why the same is to restart the application. Why not manually restart? It is recommended to use sprint-boot-devtoolsfor hot deployment restart.

There are a few things to be aware of during automatic restarts:

  • An automatic restart will log.

It can be turned off by the following configuration.

spring:
devtools:
restart:
log-condition-evaluation-delta: false
  • Exclude some resources that do not need an automatic restart.

Certain resources do not necessarily need to trigger a restart when they change.

By default, changing resources in /META-INF/maven, /META-INF/resources, /resources, /static, /publicor /templates does not trigger a restart but does trigger a live reload.

If you want to customize these exclusions, you can use the sprint.devtools.restart.exclude property.

For example, exclude only /static, /public you would set the following properties:

spring:
devtools:
restart:
exclude: "static/**,public/**"

If you want to keep these defaults and add additional exclusions, use the sprint.devtools.restart.additional-exclude property instead.

  • Custom restart classloader.

The restart function is implemented by using two class loaders. For most applications, this approach works well. However, it sometimes causes classloading issues.

By default, any open project in the IDE is loaded with the “restart” classloader, and any regular .jar files are loaded with the “base” classloader. You may need to customize something if you work on a multi-module project and not every module is imported into your IDE. To do this, you can create a meta-inf/sprint-devtools.properties file.

The sprint-devtools.properties file can contain properties prefixed with restart.exclude and restart.include.

The included element is the item that should be pulled up to the “restart” class loader, and the excluded element is the item that should be pushed down to the “Base” class loader.

The value of this property is a regular expression pattern applied to the classpath, as shown in the following example:

restart:
exclude:
companycommonlibs: "/mycorp-common-[\w\d-\.]+\.jar"
include:
projectcommon: "/mycorp-myproj-[\w\d-\.]+\.jar"

By default, it will not be packaged into a JAR.

The developer tools are automatically disabled when running a packaged application. If you start it through java-jar or other special class loaders, it will be considered as a production environment application.

If we expect to debug the application remotely, In this case, Devtool is also capable of remote debugging: remote client applications are designed to be run from within your IDE.

You need org.sprintframework.boot.devtool.RemoteSpringApplication to run with the same classpath as the remote project, you are connecting to.

The only required parameter for the application is the remote URL to which it connects.

For example, if using Eclipse or Spring Tools, and you have a project named my-app that has been to Cloud Foundry, do the following:

  • Select Run Configurations…​ from the Run menu.
  • Create a new Java Application, “Launch Configuration.”
  • Browse the my-app project.
  • Use org.springframework.boot.devtools.RemoteSpringApplication as the main class.
  • Add https://myapp.cfapps.io to Program arguments (or whatever your remote URL is).

Some libraries supported by Spring Boot use caching to improve performance. For example, template engines cache compiled templates to avoid repeated parsing of template files. Additionally, Spring MVC can add HTTP cache headers to responses when serving static resources.

While caching is very beneficial in production, it can be counterproductive during development, preventing you from seeing the changes you just made in your application. For this reason, sprint-boot-devtools disables the cache option by default.

For example, Thymeleaf provides sprint.thymeleaf.cache to set the cache of the template engine. When using the sprint-boot-devtools module, you do not need to manually set these properties, because sprint-boot-devtools will set them automatically.

So what configuration will be automatically set? You can find the corresponding default configuration in the DevToolsPropertyDefaultsPostProcessor class.

public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor {

static {
Map<String, Object> properties = new HashMap<>();
properties.put("spring.thymeleaf.cache", "false");
properties.put("spring.freemarker.cache", "false");
properties.put("spring.groovy.template.cache", "false");
properties.put("spring.mustache.cache", "false");
properties.put("server.servlet.session.persistent", "true");
properties.put("spring.h2.console.enabled", "true");
properties.put("spring.web.resources.cache.period", "0");
properties.put("spring.web.resources.chain.cache", "false");
properties.put("spring.template.provider.cache", "false");
properties.put("spring.mvc.log-resolved-exception", "true");
properties.put("server.error.include-binding-errors", "ALWAYS");
properties.put("server.error.include-message", "ALWAYS");
properties.put("server.error.include-stacktrace", "ALWAYS");
properties.put("server.servlet.jsp.init-parameters.development", "true");
properties.put("spring.reactor.debug", "true");
PROPERTIES = Collections.unmodifiableMap(properties);
}

Of course, if you don’t want the application properties to be set by sprint-boot-devtools by default, you can pass sprint.devtools.add-properties to false in your application.yml.

Global Devtools settings can be configured by adding the spring-boot-devtools.yml file to the $HOME/.confg/sprint-boot directory.

Any properties added to these files will apply to all Spring Boot applications using Devtools on your machine. For example, to configure restart to always use the trigger file, you need to add the following property to your sprint-boot-devtools file:

spring:
devtools:
restart:
trigger-file: ".reloadtrigger"

In the actual development process, I will not use the Devtool tool, because:

  • Devtool itself is based on the restart method, which is still not a real hot-replacement solution, JRebel is (it is charged).
  • If the overhead of automatic restart is not much different from the manual restart, then it is better to restart manually (restart on demand).
  • In most cases, if it is an internal modification of a method or a modification of static resources, it can be hot-updated through Rebuild (Ctrl + Shift + F9) in IDEA.
  • In addition, there is a tool spring loaded, which can realize the hot deployment of modified class files. For details, please refer to the instructions on its GitHub address.

Leave a Comment