Hot Deployment of User‑Implemented Java Interfaces Using Reflection and Spring
This article demonstrates how to design a simple Calculator interface in Java, implement it using both Spring‑managed and reflection‑based approaches, and achieve hot deployment by loading user‑provided JAR files at runtime, including code for class loading, bean registration, and cleanup.
The author, a senior architect, presents a practical solution for a requirement where a system must allow users to upload a JAR containing an implementation of a predefined interface, then hot‑deploy and switch to that implementation without restarting the application.
Define Simple Interface
A basic Calculator interface is defined with two methods: int calculate(int a, int b) and int add(int a, int b) .
public interface Calculator {
int calculate(int a, int b);
int add(int a, int b);
}Simple Implementation
The implementation class supports two injection styles: annotation‑based (managed by Spring) and reflection‑based (no Spring). The calculate method uses the annotation style, while add uses reflection.
@Service
public class CalculatorImpl implements Calculator {
@Autowired
CalculatorCore calculatorCore;
/** Annotation style */
@Override
public int calculate(int a, int b) {
int c = calculatorCore.add(a, b);
return c;
}
/** Reflection style */
@Override
public int add(int a, int b) {
return new CalculatorCore().add(a, b);
}
}The CalculatorCore bean provides the actual addition logic.
@Service
public class CalculatorCore {
public int add(int a, int b) {
return a + b;
}
}Reflection‑Based Hot Deployment
Users upload a JAR to a predefined directory. The system defines the JAR path ( jarAddress ) and URL ( jarPath ), loads the JAR with a URLClassLoader , obtains the implementation class by its fully‑qualified name, creates an instance via reflection, and invokes the desired 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‑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, effectively performing dynamic bean registration.
/** Register beans from JAR into 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());
}
}
}The utility class DeployUtils provides methods to read all class files from a JAR, determine whether a class carries Spring annotations, and transform class names into bean IDs.
/** Read all class files from a JAR */
public static Set
readJarFile(String jarAddress) throws IOException {
Set
classNameSet = new HashSet<>();
JarFile jarFile = new JarFile(jarAddress);
Enumeration
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 if a class is a Spring bean */
public static boolean isSpringBeanClass(Class
cla) {
if (cla == null || cla.isInterface() || Modifier.isAbstract(cla.getModifiers())) {
return false;
}
return cla.getAnnotation(Component.class) != null ||
cla.getAnnotation(Repository.class) != null ||
cla.getAnnotation(Service.class) != null;
}
/** Convert class name to 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);
}Removing Beans When Deleting a JAR
When a JAR is removed or replaced, the previously registered beans must also be removed from the Spring container using the same bean names.
/** Delete beans registered from a JAR */
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 Loop
A simple test continuously attempts hot deployment, catching exceptions when the JAR is not yet present and sleeping before retrying.
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);
}
}Beyond the technical tutorial, the original article also contains promotional material for ChatGPT services, private consulting, and a paid community offering AI‑related resources.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.