Hot Deployment of Custom Interface Implementations in Java Using Reflection and Spring
This article explains how to let users upload a JAR containing a custom implementation of a predefined Java interface, then hot‑deploy the JAR by loading it with a URLClassLoader and dynamically registering or deregistering Spring beans using reflection or annotation modes.
In a recent development scenario, a system requires users to upload a JAR containing a custom implementation of a predefined interface, which the system then hot‑deploys and switches to.
Define a simple interface
public interface Calculator {
int calculate(int a, int b);
int add(int a, int b);
}Simple implementation
The implementation can be managed by Spring (annotation mode) or be independent (reflection mode). The code shows a Spring‑managed class that delegates to a core component.
@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);
}
}CalculatorCore
@Service
public class CalculatorCore {
public int add(int a, int b) {
return a + b;
}
}Reflection‑based hot deployment
The uploaded JAR path is defined, then the JAR is loaded with a URLClassLoader, the implementation class is instantiated via reflection, and its methods are invoked.
private static String jarAddress = "E:/zzq/IDEA_WS/CalculatorTest/lib/Calculator.jar";
private static String jarPath = "file:/" + jarAddress;
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);
}Spring‑annotation hot deployment
If the uploaded JAR contains Spring beans, the system scans all classes, identifies those with Spring stereotypes, and registers them dynamically into the current Spring container.
public static void hotDeployWithSpring() throws Exception {
Set
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());
}
}
}Utility methods in DeployUtils read all class files from a JAR, determine whether a class carries Spring annotations, and convert a class name to a bean name with a lower‑case first letter.
public static Set
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 or replaced, the previously registered beans must be deregistered from the Spring container using the same bean names.
public static void delete() throws Exception {
Set
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 harness
A simple test loop repeatedly attempts hot deployment, catching exceptions until the JAR becomes available.
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);
}
}Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
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.