Understanding Hot Deployment and Hot Reloading in Spring Boot
Spring Boot hot deployment and hot reloading
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 spring-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 spring-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.
What is LiveLoad?
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 devtools
, so 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 spring-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 spring-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.
The Principle of Devtool? Why Does It Automatically Restart?
spring-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 relatively few, so the restart is faster.
This is why the same is to restart the application. Why not manually restart? It is recommended to use spring-boot-devtools
for 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
, /public
, or /templates
does not trigger a restart but does trigger a live reload.
If you want to customize these exclusions, you can use the spring.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 spring.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/spring-devtools.properties
file.
The spring-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"
Will Devtool Be Packaged Into 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.springframework.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 deployed 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).
Why Does Devtool Disable the Cache Option by Default?
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, spring-boot-devtools
disables the cache option by default.
For example, Thymeleaf provides spring.thymeleaf.cache
to set the cache of the template engine. When using the spring-boot-devtools
module, you do not need to manually set these properties, because spring-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 spring-boot-devtools
by default, you can pass spring.devtools.add-properties
to false in your application.yml
.
Can Devtools Do the Global Configuration for All Spring Boot Applications?
Global Devtools settings can be configured by adding the spring-boot-devtools.yml
file to the $HOME/.confg/spring-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 spring-boot-devtools
file:
spring:
devtools:
restart:
trigger-file: ".reloadtrigger"
If I Don’t Use Devtool, What Are My Options?
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.
Thank you for reading this article.
Stay tuned for more.