How to Implement Hot Deployment of Custom Java Interfaces with Spring and Reflection
This article explains how to let users upload a JAR containing a custom implementation of a predefined Java interface, then hot‑deploy the new implementation using either Spring annotation‑based injection or pure reflection, covering interface definition, bean registration, and cleanup.
During recent development a requirement arose to allow users to upload a JAR that implements a given interface, and the system must hot‑deploy the new implementation.
Simple interface definition
public interface Calculator {
int calculate(int a, int b);
int add(int a, int b);
}Implementation example
The implementation can be written in two ways: using Spring’s annotation‑based injection (the calculate method) or using plain reflection (the add method).
@Service
public class CalculatorImpl implements Calculator {
@Autowired
CalculatorCore calculatorCore;
@Override
public int calculate(int a, int b) {
int c = calculatorCore.add(a, b);
return c;
}
@Override
public int add(int a, int b) {
return new CalculatorCore().add(a, b);
}
}The CalculatorCore class provides the actual addition logic:
@Service
public class CalculatorCore {
public int add(int a, int b) {
return a + b;
}
}Reflection‑based hot deployment
When a JAR is uploaded, its path is stored in jarAddress and jarPath. The class is loaded with a URLClassLoader, instantiated via reflection, and its methods are invoked.
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‑based hot deployment
If the uploaded JAR contains Spring components, the system scans all classes, registers those annotated with @Component, @Service, or @Repository into the current Spring container.
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 utility class DeployUtils provides methods to read all class names from a JAR, determine whether a class is a Spring bean, and convert class names to bean IDs.
public static Set<String> readJarFile(String jarAddress) throws IOException { … }
public static boolean isSpringBeanClass(Class<?> cla) { … }
public static String transformName(String className) { … }Removing beans when a JAR is deleted
When a JAR is removed, the previously registered beans must be deregistered from the Spring container using the same bean name transformation.
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));
}
}
}A simple test loop repeatedly attempts hot deployment until the JAR appears, then calls the appropriate deployment method.
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.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
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.
