Backend Development 27 min read

Understanding Java ClassLoader, Custom ClassLoaders, Hot Swapping, SPI, and Java Agent Plugin Framework

This article explains the Java parent‑delegation model, shows how to implement custom ClassLoaders for class hot‑swap and jar hot‑deployment, demonstrates SPI‑based class replacement, introduces Java Agent premain/agentmain mechanisms, and presents a plugin framework that combines SPI, Java Agent, and Javassist for dynamic enhancement.

JD Retail Technology
JD Retail Technology
JD Retail Technology
Understanding Java ClassLoader, Custom ClassLoaders, Hot Swapping, SPI, and Java Agent Plugin Framework

Long ago the author wanted a tool that could instantly diagnose online problems by inserting a diagnostic package, which later evolved into Java IDE hot‑reload, Althas online data diagnosis, and eventually the desire to build custom class‑loading mechanisms.

Parent Delegation Model – The article first describes the parent delegation mechanism of Java ClassLoaders, outlining the steps from AppClassLoader to ExtensionClassLoader and finally BootstrapClassLoader, including cache checks and ClassNotFound handling.

Custom ClassLoader

public class CustomClassLoader extends ClassLoader {
// need the base directory of classes to load
private String baseDir;
public CustomClassLoader(String baseDir, String[] classes) throws IOException {
super();
this.baseDir = baseDir;
loadClassByMe(classes);
}
private void loadClassByMe(String[] classes) throws IOException {
for (int i = 0; i < classes.length; i++) {
findClass(classes[i]);
}
}
@Override
    protected Class findClass(String name) {
Class clazz = null;
StringBuffer stringBuffer = new StringBuffer(baseDir);
String className = name.replace('.', File.separatorChar) + ".class";
stringBuffer.append(File.separator + className);
File classF = new File(stringBuffer.toString());
try {
clazz = instantiateClass(name, new FileInputStream(classF), classF.length());
} catch (IOException e) { e.printStackTrace(); }
return clazz;
}
private Class instantiateClass(String name, InputStream fin, long len) throws IOException {
byte[] raw = new byte[(int) len];
fin.read(raw); fin.close();
return defineClass(name, raw, 0, raw.length);
}
}

The custom loader overrides findClass and uses defineClass to load bytecode, demonstrating a minimal hot‑swap implementation.

Class Hot‑Swap Example

package com.tw.client;
public class Foo {
public Foo() {}
public void sayHello() { System.out.println("hello world22222! (version 11)"); }
}
public static void main(String[] args) throws Exception { while (true) { run(); Thread.sleep(1000); } }
public static void run() throws Exception {
CustomClassLoader cl = new CustomClassLoader("swap", new String[]{"com.tw.client.Foo"});
Class clazz = cl.loadClass("com.tw.client.Foo");
Object foo = clazz.newInstance();
Method m = foo.getClass().getMethod("sayHello", new Class[]{});
m.invoke(foo, new Object[]{});
}

Replacing Foo.class in the swap directory changes the output, proving successful hot‑swap.

Because the class loaded by the custom loader cannot be cast to the same type loaded by the AppClassLoader, a ClassCastException occurs; the article suggests defining an interface (e.g., IFoo ) to avoid this issue.

Thread Context ClassLoader – The article shows how to set and get a thread’s context class loader using Thread.setContextClassLoader and Thread.getContextClassLoader , enabling class loading in child threads.

SPI‑Based Class Hot‑Swap

public interface HelloService { void sayHello(String name); }
public class HelloServiceProvider implements HelloService { public void sayHello(String name) { System.out.println("Hello " + name); } }
public class NameServiceProvider implements HelloService { public void sayHello(String name) { System.out.println("Hi, your name is " + name); } }

By placing the fully‑qualified provider class names in META-INF/services/com.tinywhale.deploy.spi.HelloService and using ServiceLoader.load(HelloService.class) , the program can dynamically load and invoke the services, and removing a provider entry disables its output.

Jar Hot‑Deployment

public class BizClassLoader extends URLClassLoader { public BizClassLoader(URL[] urls) { super(urls); } }

Using BizClassLoader to load a fat jar from a swap directory allows the entire jar to be hot‑replaced; the article notes that long‑running tasks should be avoided before closing the loader.

Java Agent Basics

public static void premain(String agentArgs, Instrumentation inst) { /* executed before main */ }
public static void agentmain(String agentArgs, Instrumentation inst) { /* executed after attach */ }

Configuration of MANIFEST.MF with Premain-Class and Agent-Class is required. The article demonstrates running the agent via -javaagent and attaching it at runtime using VirtualMachine.attach .

Plugin Framework (SPI + Java Agent + Javassist)

The framework defines an IPluginExecuteStrategy interface, implements PluginPreMainExecutor and PluginAgentMainExecutor to scan plugins annotated with @PreMainCondition or @AgentMainCondition , and uses a factory to create execution contexts.

public class AgentPluginPreWrapper { public static void premain(String args, Instrumentation inst) { AgentPluginContextFactory.makeAgentPreExecuteContext().execute(args, inst); } }
public class AgentPluginMainWrapper { public static void agentmain(String args, Instrumentation inst) { AgentPluginContextFactory.makeAgentMainExecuteContext().execute(args, inst); } }

Bytecode enhancement is performed by ByteCodeBizInvoker , which modifies Spring’s ComponentScanBeanDefinitionParser to automatically add the framework’s package to the scan path using Javassist.

Remote Plugin Loading

public class TinyPluginClassLoader extends URLClassLoader { public TinyPluginClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } public void addURL(URL url) { super.addURL(url); } }

Methods are provided to load classes from local or HTTP‑based jar URLs, extract plugin classes that implement a given interface, and invoke them via ServiceLoader . This enables plugins to be loaded from the network without rebuilding the host application.

Conclusion

The article walks the reader from the fundamentals of the Java ClassLoader hierarchy, through custom loaders for class and jar hot‑swap, SPI‑based service replacement, Java Agent instrumentation, and finally a full‑featured plugin framework that supports both local and remote plugins, illustrating each step with concrete code examples.

JavaClassLoaderSPIJavaAgentHotSwapPluginFramework
JD Retail Technology
Written by

JD Retail Technology

Official platform of JD Retail Technology, delivering insightful R&D news and a deep look into the lives and work of technologists.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.