hotswap-classloader is a dynamic class loader based on the JVM. It utilizes the HotSwapWatcher and HotSwapClassloader technologies to dynamically detect modifications to class files. This project was inspired by the hot loading design of jfinal-undertow. See Reference
Key Features:
- Achieves rapid application hot reloading, with test results showing hot reloads completed in approximately 1 second.
Loading Speed:
- Upon integration with spring-boot, directly starting spring-boot in eclipse, and making changes to the controller followed by pressing Ctrl+S to save, the application will automatically restart and parse the class. The entire process completes within 1 second.
Comparable Products:
- springloaded
- spring-boot-devtools
- JRebel
- Add Dependency
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>hotswap-classloader</artifactId>
<!--https://central.sonatype.com/artifact/com.litongjava/hotswap-classloader-->
<version>1.2.2/version>
</dependency>
- Add Configuration File
Create aconfig.properties
file undersrc/main/resource/
and add the following content:
mode=dev
- Modify the Startup Class Code
ReplaceSpringApplication.run(Application.class, args);
withSpringApplicationWrapper.run(Application.class, args);
.
Example:
package com.litongjava.spring.boot.v216;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.litongjava.hotswap.wrapper.spring.boot.SpringApplicationWrapper;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplicationWrapper.run(Application.class, args);
}
}
Note: SpringApplicationWrapper
reads the mode
key value from the config.properties
file. If the value is dev
, it starts the hotswapwather to monitor class changes and enables hot reloading; otherwise, it does not activate.
Upon completing the above steps, you can refer to this project for integration:
View the integrated project
Calls ForkApp.run in its own startup
//params: startup class, startup parameters, hot load, restart class
ForkApp.run(SklearnWebApp.class, args, true, new SelfRestart());
For example
package com.litongjava.tio.boot.djl;
import org.tio.utils.jfinal.P;
import com.litongjava.hotswap.wrapper.forkapp.ForkApp;
public class SklearnWebApp {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// Initialize the server and start the server
P.use("app.properties");
// Diagnostic.setDebug(true);
// TioApplicationWrapper.run(SklearnWebApp.class, args);
ForkApp.run(SklearnWebApp.class, args, true, new SelfRestart());
long end = System.currentTimeMillis();
System.out.println("started:" + (end - start) + "(ms)");
}
}
Write SelfRestart to implement the methods in RestartServer
package com.litongjava.tio.boot.djl;
import com.litongjava.hotswap.debug.Diagnostic;
import com.litongjava.hotswap.kit.HotSwapUtils;
import com.litongjava.hotswap.server.RestartServer;
import com.litongjava.hotswap.wrapper.forkapp.ForkAppBootArgument;
import com.litongjava.tio.boot.TioApplication;
import com.litongjava.tio.boot.context.Context;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SelfRestart implements RestartServer {
public boolean isStarted() {
return ForkAppBootArgument.getContext().isRunning();
}
public void restart() {
System.err.println("loading");
long start = System.currentTimeMillis();
stop();
// get a new ClassLoader
ClassLoader hotSwapClassLoader = HotSwapUtils.newClassLoader();
if (Diagnostic.isDebug()) {
log.info("new classLoader:{}", hotSwapClassLoader);
}
// Set the context loader
Thread.currentThread().setContextClassLoader(hotSwapClassLoader);
// get startup class and args
Class<?> clazz = ForkAppBootArgument.getBootClazz();
String[] args = ForkAppBootArgument.getArgs();
// start
start(clazz, args);
long end = System.currentTimeMillis();
System.err.println("Loading complete in " + (end - start) + " ms (^_^)\n");
}
@Override
public void start(Class<?> primarySource, String[] args) {
Context context = TioApplication.run(primarySource, args);
ForkAppBootArgument.setContext(context);
}
@Override
public void stop() {
ForkAppBootArgument.getContext().close();
}
}
HotSwapWatcher mainly listens to modifications of class files under target/classes
to trigger hot reloading. However, by default in IDEA, there is no automatic compilation, causing no changes to the files under target/classes
. There are two solutions:
- Use the Ctrl + F9 shortcut to trigger compilation. (Test failed in IntelliJ IDEA 2019.3.3 (Ultimate Edition))
- Configure IDEA to enable automatic compilation, similar to eclipse.
-
Automatically Build Project
Search for "compiler" in settings, then check "build project automatically".
-
Allow Automatic Building Even When a Development Application is Running
Search for "make" in settings, then check "Allow auto-make to start even if developed application is currently running".
-
Adjust Delay Time
Use the Ctrl+Shift+Alt+/ shortcut, select "Registry...", then adjust the following configurations:
compiler.automake.postpone.when.idle.less.than
: Default is 3000, change to 100.compiler.automake.trigger.delay
: Default value is 3000, change to 100.compiler.document.save.trigger.delay
: Default is 1500, change to 100.
- Cancel Automatic Code Saving
In "File" -> "Settings" -> "Appearance & Behavior" -> "System Settings", uncheck the following options:
- Older versions: Uncheck "Save files on frame deactivation" and "Synchronize files on frame or editor tab activation".
- Newer versions: Uncheck "Save files if tab IDE is idle for 10 seconds" and "Save file when switching to a different application or a built-in terminal".
- Display "modified" Mark
After modifying a file, a "star" mark will be displayed in the code editing window's tab area. Navigate to "File" -> "Settings" -> "Editor" -> "General" -> "Editor Tabs", then check "Mark modified(*)".
After completing the above settings, modifying a file and saving it in IDEA will result in the file being automatically compiled, and the application will automatically restart with hot reloading applied.
Note: There might be an issue when a package contains only one .java
file. For more details, please check here.
If you aim to start the spring-boot project from the command line using mvn spring-boot:run
, the default class loader is plexus-classworlds
. To use this class loader, you need to follow these steps:
- Add the aforementioned dependency.
- Modify the startup class.
- Add the following configuration to
pom.xml
to enable plugin support for hot startup:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
<fork>true</fork>
<mainClass>${start-class}</mainClass>
</configuration>
</plugin>
No setup required, natively supported. Modify a Java file and it will be loaded automatically after saving. Development experience is better than IDEA
No setup required, natively supported. Modify a Java file and it will be loaded automatically after saving. Development experience is better than IDEA
- Start the project using:
mvn spring-boot:run
After starting spring-boot, adding a method to the controller, and pressing Ctrl+S to save, the HotSwapClassloader detects file changes and automatically reloads the code. This process is completed in approximately 0.8 seconds.
After starting spring-boot, adding a method to the controller, and pressing Ctrl+S to save, the HotSwapClassloader detects the file changes and automatically reloads the code. However, in IDEA, due to a compilation delay of about 10 seconds, the entire reloading process takes approximately 10.8 seconds.
When starting the project from the command line using mvn spring-boot:run
, you can modify the code in eclipse or IDEA for testing. This test is based on a large project. A regular startup takes 9.5 seconds, while hot reloading takes 3.4 seconds.
Click to view the video demonstration
<iframe width="720" height="405" frameborder="0" src="https://www.ixigua.com/iframe/7091662497010156063?autoplay=0" referrerpolicy="unsafe-url" allowfullscreen></iframe>