How to Hot‑Deploy User‑Provided JARs with Spring and Reflection
This guide explains how to let users upload a JAR that implements a predefined interface, then hot‑deploy the new implementation at runtime using either Spring annotation‑based bean registration or pure Java reflection, complete with utility methods for loading, registering, and cleaning up beans.
During a recent project a requirement arose to allow users to provide their own implementation of a given interface, package it as a JAR, upload it to the system, and have the system hot‑deploy and switch to the new implementation.
Define a simple interface
public interface Calculator {
int calculate(int a, int b);
int add(int a, int b);
}A simple implementation of the interface
The implementation can be provided in two ways: using Spring’s annotation‑based management (annotation mode) or without Spring (reflection mode).
@Service
public class CalculatorImpl implements Calculator {
@Autowired
CalculatorCore calculatorCore;
/** annotation mode */
@Override
public int calculate(int a, int b) {
int c = calculatorCore.add(a, b);
return c;
}
/** reflection mode */
@Override
public int add(int a, int b) {
return new CalculatorCore().add(a, b);
}
}The CalculatorCore bean is used to verify that, in annotation mode, the full bean dependency graph can be constructed and registered in the current Spring container.
@Service
public class CalculatorCore {
public int add(int a, int b) {
return a + b;
}
}Reflection method hot deployment
Users upload the JAR to a predefined directory. The path variables are defined as follows:
private static String jarAddress = "E:/zzq/IDEA_WS/CalculatorTest/lib/Calculator.jar";
private static String jarPath = "file:/" + jarAddress;The system loads the JAR with the current thread’s class loader, obtains the implementation class by its fully‑qualified name, creates an instance via reflection, and invokes the method:
/** Hot‑load Calculator implementation using reflection */
public static void hotDeployWithReflect() throws Exception {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());
Class clazz = urlClassLoader.loadClass("com.nci.cetc15.calculator.impl.CalculatorImpl");
Calculator calculator = (Calculator) clazz.newInstance();
int result = calculator.add(1, 2);
System.out.println(result);
}Annotation method hot deployment
If the uploaded JAR contains Spring components, the system scans all classes, registers those annotated with Spring stereotypes into the current Spring container, effectively performing dynamic bean registration.
/** Dynamically register beans from the uploaded JAR */
public static void hotDeployWithSpring() throws Exception {
Set<String> classNameSet = DeployUtils.readJarFile(jarAddress);
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());
for (String className : classNameSet) {
Class clazz = urlClassLoader.loadClass(className);
if (DeployUtils.isSpringBeanClass(clazz)) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
defaultListableBeanFactory.registerBeanDefinition(DeployUtils.transformName(className), beanDefinitionBuilder.getBeanDefinition());
}
}
}The DeployUtils class provides helper methods for reading JAR entries, detecting Spring‑annotated classes, and generating bean names.
/** Read all class files from a JAR */
public static Set<String> readJarFile(String jarAddress) throws IOException {
Set<String> classNameSet = new HashSet<>();
JarFile jarFile = new JarFile(jarAddress);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String name = jarEntry.getName();
if (name.endsWith(".class")) {
String className = name.replace(".class", "").replaceAll("/", ".");
classNameSet.add(className);
}
}
return classNameSet;
}
/** Determine whether a class has a Spring stereotype */
public static boolean isSpringBeanClass(Class<?> cla) {
if (cla == null) return false;
if (cla.isInterface()) return false;
if (Modifier.isAbstract(cla.getModifiers())) return false;
if (cla.getAnnotation(Component.class) != null) return true;
if (cla.getAnnotation(Repository.class) != null) return true;
if (cla.getAnnotation(Service.class) != null) return true;
return false;
}
/** Convert class name to Spring bean name (lower‑case first letter) */
public static String transformName(String className) {
String tmp = className.substring(className.lastIndexOf('.') + 1);
return tmp.substring(0, 1).toLowerCase() + tmp.substring(1);
}Delete JAR and remove beans from Spring container
When a JAR is removed or switched, the previously registered beans must also be removed from the Spring context.
/** Delete beans registered from a JAR */
public static void delete() throws Exception {
Set<String> classNameSet = DeployUtils.readJarFile(jarAddress);
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());
for (String className : classNameSet) {
Class clazz = urlClassLoader.loadClass(className);
if (DeployUtils.isSpringBeanClass(clazz)) {
defaultListableBeanFactory.removeBeanDefinition(DeployUtils.transformName(className));
}
}
}Test
A test class simulates the user uploading a JAR. It repeatedly attempts hot deployment and sleeps when the JAR is not yet present.
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
while (true) {
try {
hotDeployWithReflect();
// hotDeployWithSpring();
// delete();
} catch (Exception e) {
e.printStackTrace();
Thread.sleep(1000 * 10);
}
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
