laitimes

Spring Loaded Code Hot Update Practice and Principle Analysis

author:JD Cloud developer

1. Introduction

Pain points for developers in coding efficiency and rapid iteration include:

1. After modifying the code, you need to restart the application frequently, resulting in inefficient development;

2. When debugging in real time, the results of code modification cannot be seen immediately;

3. In large-scale projects, the time cost of restart is high.

In response to these problems, this article will explore in depth how to use Spring Loaded hot update technology to improve development efficiency and reduce compilation and restart time. Analyze the hot update principle of Spring Loaded, as well as the operations and considerations required in the actual application process.

2. Introduction to the framework

Spring Loaded is a JVM agent for reloading class file changes whilst a JVM is running. It transforms classes at loadtime to make them amenable to later reloading. Unlike 'hot code replace' which only allows simple changes once a JVM is running (e.g. changes to method bodies), Spring Loaded allows you to add/modify/delete methods/fields/constructors. The annotations on types/methods/fields/constructors can also be modified and it is possible to add/remove/change values in enum types.

Spring Loaded is a JVM proxy that can reload changes to class files while the JVM is running. It converts the class on load so that it can be reloaded later. Unlike Hot Code Substitution, which only allows simple changes (such as changing the body of a method) while the JVM is running, Spring Loaded allows you to add/modify/delete methods/fields/constructors. You can also modify annotations on types/methods/fields/constructors, and you can add/remove/change values in enumerated types.

3. How to use it

3.1 Download the Agent plugin

https://repo1.maven.org/maven2/org/springframework/springloaded/1.2.8.RELEASE/springloaded-1.2.8.RELEASE.jar           

3.2 Introduce Agent plugin

Add the following parameters to the JVM startup command

-javaagent:/Users/you/runtime/springloaded-1.2.8.RELEASE.jar -noverify           

3.3 Modify and recompile

After modifying the code, execute the Build->Recompile command, and you can see that the running logic of the program changes after the class reloaded is completed

Spring Loaded Code Hot Update Practice and Principle Analysis

4. Principle analysis

4.1 Code compilation and analysis

Let's start with a piece of source code, which is an RpcService class that defines the target field, the targetStatic static field, and the say method, and now we'll compile it.

public class RpcService {
    private String target = "rpc";
    private static String targetStatic = "rpc static";
    public String say() {
        return "RpcService say hello SpringLoaded" + target;
    }
}           

SpringLoaded adds some trace record fields after compiling the class, and adds method interception judgment.

public static ReloadableType r$type = TypeRegistry.getReloadableType(0, 1);

public transient ISMgr r$fields;

public static final SSMgr r$sfields;

public String hello() {
    if (r$type.changed(0) == 1) {
        return r$type.fetchLatest()).say(this);
    }
    String targetNew = TypeRegistry.instanceFieldInterceptionRequired(1, "target") ? (String)r$get(this, "target") : this.target;
    return "RpcService say hello SpringLoaded" + targetNew;
}           

We can use functions such as getDeclaredField and getDeclared Method to obtain class members and method information at runtime when the code runs, and we can see that the enhanced class has more fields and methods as follows.

Spring Loaded Code Hot Update Practice and Principle Analysis
Spring Loaded Code Hot Update Practice and Principle Analysis

In the compiled code, we can see that the RpcService class contains some new fields and methods that have been added to the Spring Loaded framework.

•r$type is a static variable of type ReloadableType. This field is used to represent the overloadable type of the current class, which contains the latest bytecode and other relevant information for the current class.

  • The r$get and r$set methods are methods used to get the value of an instance field and handle the interception and replacement of the field.
  • ___clinit___ method is the method used to execute the static initialization block of the class.
  • ___init___() method is the method used to handle the constructor of the class.
  • A code snippet has been added to the say() method to determine if the class has changed, and if so, call the say() method in the latest overloadable type to get the result. Otherwise, continue with the original method body. In the method body, a code snippet has also been added to determine whether the local variable needs to be intercepted, and if necessary, use the r$get() method to get the value of the non-static variable traget, and replace the original variable value with it.

4.2 Run Process Analysis

1. When the application starts, Spring Loaded looks for all classes in the target classpath, uses a custom class loader to load these classes in ClassPreProcessor, redefines them and stores them in TypeRegistry for caching, change comparison and dependency maintenance.

2. Register a file change listener FileChangeListener, when a class file is modified, Spring Loaded will detect this change and reload the class file.

When a class is reloaded, Spring Loaded will try to compare the signature and inheritance of the class unchanged, if the new class definition is compatible with the previous class definition, then Spring Loaded will update the object reference in the application to point to the new class definition.

5. Summary

Spring-loaded uses Java's Instrumentation API to specify an agent at JVM startup, enabling it to intercept before the target class is loaded, and parse the bytecode of the target class into an abstract syntax tree (AST) through the ASM library, which is then modified. The modified content includes adding, deleting, replacing methods, modifying the method body, adding fields, etc., and finally replacing the target class, changing its logic, and realizing hot updates to the code.

6. Extended content

  • Jrepel can also implement similar hot update functions, and it is more efficient and stable. Jrebel official website
  • Spring-boot-devtools can also speed up development, but its solution is more like a hot restart. Spring Boot Devtools Restarter principle
  • How to implement a hot update function yourself? The ideas are similar, and the implementation has its own merits. How to implement a hot load yourself? How do I define my own classloader?

Author: JD Retail Cheng Xiao

Source: JD Cloud Developer Community