{"id":21803021,"url":"https://github.com/litongjava/hotswap-classloader","last_synced_at":"2025-07-26T20:11:26.032Z","repository":{"id":57726235,"uuid":"379579520","full_name":"litongjava/hotswap-classloader","owner":"litongjava","description":"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. ","archived":false,"fork":false,"pushed_at":"2024-10-02T10:49:55.000Z","size":12457,"stargazers_count":14,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-16T01:16:09.646Z","etag":null,"topics":["classloader","hotswap","java","spring","spring-boot"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/litongjava.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-23T11:27:21.000Z","updated_at":"2024-10-14T06:45:13.000Z","dependencies_parsed_at":"2023-10-02T01:52:33.826Z","dependency_job_id":"b1970845-4e99-48f4-ba65-f43d403a8fc7","html_url":"https://github.com/litongjava/hotswap-classloader","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/litongjava%2Fhotswap-classloader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/litongjava%2Fhotswap-classloader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/litongjava%2Fhotswap-classloader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/litongjava%2Fhotswap-classloader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/litongjava","download_url":"https://codeload.github.com/litongjava/hotswap-classloader/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226744545,"owners_count":17675024,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["classloader","hotswap","java","spring","spring-boot"],"created_at":"2024-11-27T11:37:19.338Z","updated_at":"2024-11-27T11:37:20.182Z","avatar_url":"https://github.com/litongjava.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hotswap-classloader: Dynamic Hot Reloading\n[English](readme.md) | [中文](readme_cn.md)\n## 1. Introduction\n\n**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.\n[See Reference](https://gitee.com/jfinal/jfinal-undertow/tree/master/src/main/java/com/jfinal/server/undertow/hotswap)\n\n**Key Features:**\n- Achieves rapid application hot reloading, with test results showing hot reloads completed in approximately 1 second.\n\n**Loading Speed:**\n- 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.\n\n**Comparable Products:**\n- springloaded\n- spring-boot-devtools\n- JRebel\n\n## 2. Integration and Usage\n\n### 2.1 Integration with spring-boot\n\n1. **Add Dependency**\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.litongjava\u003c/groupId\u003e\n  \u003cartifactId\u003ehotswap-classloader\u003c/artifactId\u003e\n  \u003c!--https://central.sonatype.com/artifact/com.litongjava/hotswap-classloader--\u003e\n  \u003cversion\u003e1.2.2/version\u003e\n\u003c/dependency\u003e\n```\n\n2. **Add Configuration File**  \n   Create a `config.properties` file under `src/main/resource/` and add the following content:\n```\nmode=dev\n```\n\n3. **Modify the Startup Class Code**  \n   Replace `SpringApplication.run(Application.class, args);` with `SpringApplicationWrapper.run(Application.class, args);`.\n\nExample:\n\n```java\npackage com.litongjava.spring.boot.v216;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport com.litongjava.hotswap.wrapper.spring.boot.SpringApplicationWrapper;\n\n@SpringBootApplication\npublic class Application {\n  public static void main(String[] args) {\n    SpringApplicationWrapper.run(Application.class, args);\n  }\n}\n```\n\nNote: `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.\n\nUpon completing the above steps, you can refer to this project for integration:  \n[View the integrated project](https://gitee.com/ppnt/java-ee-spring-boot-study/tree/master/maven/java-ee-spring-boot-2.1.6-study/java-ee-spring-boot-2.1.6-hello)\n\n### 2.2 Integration with other framework\nCalls ForkApp.run in its own startup\n```\n//params: startup class, startup parameters, hot load, restart class\nForkApp.run(SklearnWebApp.class, args, true, new SelfRestart());\n```\nFor example\n```\npackage com.litongjava.tio.boot.djl;\n\nimport org.tio.utils.jfinal.P;\n\nimport com.litongjava.hotswap.wrapper.forkapp.ForkApp;\n\npublic class SklearnWebApp {\n\n  public static void main(String[] args) throws Exception {\n    long start = System.currentTimeMillis();\n    // Initialize the server and start the server\n    P.use(\"app.properties\");\n//     Diagnostic.setDebug(true);\n//    TioApplicationWrapper.run(SklearnWebApp.class, args);\n     ForkApp.run(SklearnWebApp.class, args, true, new SelfRestart());\n    long end = System.currentTimeMillis();\n    System.out.println(\"started:\" + (end - start) + \"(ms)\");\n  }\n}\n```\nWrite SelfRestart to implement the methods in RestartServer\n\n```\npackage com.litongjava.tio.boot.djl;\n\nimport com.litongjava.hotswap.debug.Diagnostic;\nimport com.litongjava.hotswap.kit.HotSwapUtils;\nimport com.litongjava.hotswap.server.RestartServer;\nimport com.litongjava.hotswap.wrapper.forkapp.ForkAppBootArgument;\nimport com.litongjava.tio.boot.TioApplication;\nimport com.litongjava.tio.boot.context.Context;\n\nimport lombok.extern.slf4j.Slf4j;\n\n@Slf4j\npublic class SelfRestart implements RestartServer {\n  public boolean isStarted() {\n    return ForkAppBootArgument.getContext().isRunning();\n  }\n\n  public void restart() {\n    System.err.println(\"loading\");\n    long start = System.currentTimeMillis();\n\n    stop();\n    // get a new ClassLoader\n    ClassLoader hotSwapClassLoader = HotSwapUtils.newClassLoader();\n    if (Diagnostic.isDebug()) {\n      log.info(\"new classLoader:{}\", hotSwapClassLoader);\n    }\n\n    // Set the context loader\n    Thread.currentThread().setContextClassLoader(hotSwapClassLoader);\n\n    // get startup class and args\n    Class\u003c?\u003e clazz = ForkAppBootArgument.getBootClazz();\n    String[] args = ForkAppBootArgument.getArgs();\n    // start \n    start(clazz, args);\n    long end = System.currentTimeMillis();\n    System.err.println(\"Loading complete in \" + (end - start) + \" ms (^_^)\\n\");\n  }\n\n  @Override\n  public void start(Class\u003c?\u003e primarySource, String[] args) {\n    Context context = TioApplication.run(primarySource, args);\n    ForkAppBootArgument.setContext(context);\n  }\n\n  @Override\n  public void stop() {\n    ForkAppBootArgument.getContext().close();\n  }\n}\n```\n## 3.Support for IDE\n\n### 3.1 Support for IDEA\n\n### 3.2 Support for IDEA 2021.3.3\n#### 3.2.1 Version Information\nIDEA version is as follows:  \n![](readme_files/1.jpg)\n\n#### 3.2.2 Why Hot Reload Configuration is Needed\nHotSwapWatcher 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:\n1. Use the Ctrl + F9 shortcut to trigger compilation. (Test failed in IntelliJ IDEA 2019.3.3 (Ultimate Edition))\n2. Configure IDEA to enable automatic compilation, similar to eclipse.\n\n#### 3.2.3 IDEA Hot Reload Settings\n\n1. **Automatically Build Project**  \n   Search for \"compiler\" in settings, then check \"build project automatically\".  \n   ![](readme_files/2.jpg)\n\n2. **Allow Automatic Building Even When a Development Application is Running**  \n   Search for \"make\" in settings, then check \"Allow auto-make to start even if developed application is currently running\".  \n   ![](readme_files/3.jpg)\n\n3. **Adjust Delay Time**  \n   Use the Ctrl+Shift+Alt+/ shortcut, select \"Registry...\", then adjust the following configurations:\n\n- `compiler.automake.postpone.when.idle.less.than`: Default is 3000, change to 100.\n- `compiler.automake.trigger.delay`: Default value is 3000, change to 100.\n- `compiler.document.save.trigger.delay`: Default is 1500, change to 100.\n\n4. **Cancel Automatic Code Saving**  \n   In \"File\" -\u003e \"Settings\" -\u003e \"Appearance \u0026 Behavior\" -\u003e \"System Settings\", uncheck the following options:\n\n- Older versions: Uncheck \"Save files on frame deactivation\" and \"Synchronize files on frame or editor tab activation\".\n- 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\".\n\n5. **Display \"modified\" Mark**  \n   After modifying a file, a \"star\" mark will be displayed in the code editing window's tab area. Navigate to \"File\" -\u003e \"Settings\" -\u003e \"Editor\" -\u003e \"General\" -\u003e \"Editor Tabs\", then check \"Mark modified(*)\".\n\nAfter 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.\n\n**Note**: There might be an issue when a package contains only one `.java` file. For more details, please check [here](https://jfinal.com/share/2436).\n\n### 3.3 Support for spring-boot-maven-plugin\n\nIf 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:\n\n1. Add the aforementioned dependency.\n2. Modify the startup class.\n3. Add the following configuration to `pom.xml` to enable plugin support for hot startup:\n\n```xml\n\u003cplugin\u003e\n  \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n  \u003cartifactId\u003espring-boot-maven-plugin\u003c/artifactId\u003e\n  \u003cconfiguration\u003e\n    \u003cincludeSystemScope\u003etrue\u003c/includeSystemScope\u003e\n    \u003cfork\u003etrue\u003c/fork\u003e\n    \u003cmainClass\u003e${start-class}\u003c/mainClass\u003e\n  \u003c/configuration\u003e\n\u003c/plugin\u003e\n```\n\n### 1.4 Eclipse\nNo setup required, natively supported. Modify a Java file and it will be loaded automatically after saving. Development experience is better than IDEA\n### 1.5 Visual Studio Code\nNo setup required, natively supported. Modify a Java file and it will be loaded automatically after saving. Development experience is better than IDEA\n\n4. Start the project using:\n```\nmvn spring-boot:run\n```\n\n## 4. Demonstrative Screenshots of Usage\n\n### 4.1 Eclipse Testing Results\nAfter 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.\n\n![Eclipse Testing Results](doc/images/hotswap-classloader-spring-boot-elipse-test.gif)\n\n### 4.2 IDEA Testing Results\n\nAfter 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.\n\n![IDEA Testing Results](doc/images/hotswap-classloader-spring-boot-idea-test.gif)\n\n### 4.3 Command Line Testing Results\n\nWhen 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.\n\n[Click to view the video demonstration](https://www.ixigua.com/iframe/7091662497010156063?autoplay=0)\n\n\u003ciframe width=\"720\" height=\"405\" frameborder=\"0\" src=\"https://www.ixigua.com/iframe/7091662497010156063?autoplay=0\" referrerpolicy=\"unsafe-url\" allowfullscreen\u003e\u003c/iframe\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flitongjava%2Fhotswap-classloader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flitongjava%2Fhotswap-classloader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flitongjava%2Fhotswap-classloader/lists"}