How to Hot-Deploy Custom Java Interfaces with Spring and Reflection
This article demonstrates how to define a simple Calculator interface, implement it via both Spring-managed and reflection-based approaches, and achieve hot deployment by uploading JAR files, dynamically loading classes, registering or deregistering beans in the Spring container, and testing the process.
Define a Simple Interface
Using a calculator example, the interface is defined as:
public interface Calculator {
int calculate(int a, int b);
int add(int a, int b);
}A Simple Implementation
The implementation supports two modes: an annotation‑based mode managed by Spring and a reflection‑based mode that does not depend on Spring.
@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);
}
}The CalculatorCore class provides the actual addition logic:
@Service
public class CalculatorCore {
public int add(int a, int b) {
return a + b;
}
}Hot Deployment with Reflection
After a user uploads a JAR to a predefined directory, the system loads the JAR using a URLClassLoader, obtains the implementation class by its fully‑qualified name, creates an instance via reflection, and invokes the add 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);
}Hot Deployment with Spring
If the uploaded JAR contains Spring components, the system scans all classes, registers those annotated with @Component, @Service, or @Repository as beans in the current Spring container.
/** Register beans from uploaded JAR into Spring */
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());
}
}
}Utility methods used:
/** Read all class files from a JAR */
public static Set<String> readJarFile(String jarAddress) throws IOException { ... }
/** Determine whether a class is a Spring bean */
public static boolean isSpringBeanClass(Class<?> cla) { ... }
/** Convert class name to bean name (lower‑case first letter) */
public static String transformName(String className) { ... }Removing Beans When Deleting a JAR
When a JAR is removed or replaced, the previously registered beans must be deregistered from the Spring container using the same bean names.
/** Delete beans registered from a JAR */
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 Example
A test loop continuously 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);
}
}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 High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
