Hot Deployment of Java Interface Implementations Using Reflection and Spring
This article demonstrates how to design a simple Calculator interface, provide both annotation‑based and reflection‑based implementations, and achieve hot deployment by loading user‑supplied JAR files at runtime, including dynamic bean registration and removal in a Spring container.
The article describes a scenario where a system allows users to upload a JAR containing an implementation of a predefined interface, and the system hot‑deploys and switches the implementation at runtime.
Interface definition
public interface Calculator {
int calculate(int a, int b);
int add(int a, int b);
}Implementation class shows two methods: calculate (annotation‑based) and add (reflection‑based), with Spring @Service annotation and an injected CalculatorCore.
@Service
public class CalculatorImpl implements Calculator {
@Autowired
CalculatorCore calculatorCore;
// annotation way
@Override
public int calculate(int a, int b) {
int c = calculatorCore.add(a, b);
return c;
}
// reflection way
@Override
public int add(int a, int b) {
return new CalculatorCore().add(a, b);
}
}CalculatorCore provides the actual addition logic.
@Service
public class CalculatorCore {
public int add(int a, int b) {
return a + b;
}
}Reflection‑based hot deployment loads the uploaded JAR with a URLClassLoader, obtains the implementation class by its fully‑qualified name, creates an instance, and invokes the method.
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‑based hot deployment scans all classes in the JAR, registers those annotated as Spring beans into the current BeanFactory, and thus makes them available in the 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 include reading all class files from a JAR, checking whether a class carries Spring annotations, and converting a class name to a bean name.
public static Set
readJarFile(String jarAddress) throws IOException { ... }
public static boolean isSpringBeanClass(Class
cla) { ... }
public static String transformName(String className) { ... }When a JAR is removed, the corresponding beans are also deregistered from the Spring container.
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));
}
}
}A test class demonstrates a loop that repeatedly attempts hot deployment, sleeping when the JAR is not yet present.
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);
}
}The article concludes with a call to share the content and join the community.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow 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.