How to Hot‑Deploy Custom Java Interface Implementations via JARs and Spring
This guide shows how to let users upload a JAR that contains a custom implementation of a predefined Java interface, then hot‑replace the implementation at runtime using either Spring‑managed bean injection or pure reflection, with full code examples and a test harness.
Define a Simple Interface
We start with a minimal calculator interface that declares 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 of the Interface
The implementation class demonstrates two ways a user might provide the logic: an annotation‑based approach (managed by Spring) for calculate and a reflection‑based approach for add. The class injects a CalculatorCore bean to illustrate Spring dependency wiring.
@Service
public class CalculatorImpl implements Calculator {
@Autowired
CalculatorCore calculatorCore;
// Annotation‑based method
@Override
public int calculate(int a, int b) {
int c = calculatorCore.add(a, b);
return c;
}
// Reflection‑based method
@Override
public int add(int a, int b) {
return new CalculatorCore().add(a, b);
}
}The CalculatorCore simply adds two integers:
@Service
public class CalculatorCore {
public int add(int a, int b) {
return a + b;
}
}Hot Deployment Using Reflection
When a user uploads a JAR, the system records the JAR’s absolute path ( jarAddress) and builds a URL ( jarPath). The hot‑deployment method loads the JAR with a URLClassLoader, obtains the implementation class by its fully‑qualified name, instantiates it via reflection, and invokes the add method.
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);
}Hot Deployment Using Spring Annotation Scanning
If the uploaded JAR contains Spring components, the system must scan the JAR, identify classes annotated with Spring stereotypes, and register them into the existing Spring container. The process re‑uses the same class‑loader logic, then iterates over every discovered class name.
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 three key methods: readJarFile(String jarAddress) – iterates over a JAR’s entries, extracts class names ending with .class, converts path separators to dots, and returns a Set<String> of fully‑qualified class names. isSpringBeanClass(Class<?> cla) – checks whether a class is a concrete, non‑interface, non‑abstract type and whether it carries one of the Spring annotations @Component, @Repository, or @Service. transformName(String className) – converts the simple class name to a bean name by lower‑casing the first character (e.g., CalculatorImpl → calculatorImpl).
Removing Beans When a JAR Is Deleted
When a JAR is replaced or removed, the previously registered beans must be deregistered from the Spring container. The removal logic mirrors the registration process: load the JAR, find Spring beans, and call removeBeanDefinition with the same transformed bean name.
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 Harness
A simple test program repeatedly attempts to hot‑deploy the JAR. It creates a Spring ApplicationContext, obtains the underlying DefaultListableBeanFactory, and runs an infinite loop that calls hotDeployWithReflect(). If the JAR is not yet present, an exception is caught, printed, and the thread sleeps for ten seconds before retrying, allowing a developer to drop the JAR into the monitored directory manually.
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);
}
}Source: https://blog.csdn.net/zhangzhiqiang_0912
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.
Java Web Project
Focused on Java backend technologies, trending internet tech, and the latest industry developments. The platform serves over 200,000 Java developers, inviting you to learn and exchange ideas together. Check the menu for Java learning resources.
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.
