How to Implement Hot Deployment of Custom Java Interfaces with Spring and Reflection

This article explains how to let users upload a JAR containing a custom implementation of a predefined Java interface, then hot‑deploy the new implementation using either Spring annotation‑based injection or pure reflection, covering interface definition, bean registration, and cleanup.

Architect
Architect
Architect
How to Implement Hot Deployment of Custom Java Interfaces with Spring and Reflection

During recent development a requirement arose to allow users to upload a JAR that implements a given interface, and the system must hot‑deploy the new implementation.

Simple interface definition

public interface Calculator {
    int calculate(int a, int b);
    int add(int a, int b);
}

Implementation example

The implementation can be written in two ways: using Spring’s annotation‑based injection (the calculate method) or using plain reflection (the add method).

@Service
public class CalculatorImpl implements Calculator {
    @Autowired
    CalculatorCore calculatorCore;

    @Override
    public int calculate(int a, int b) {
        int c = calculatorCore.add(a, b);
        return c;
    }

    @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;
    }
}

Reflection‑based hot deployment

When a JAR is uploaded, its path is stored in jarAddress and jarPath. The class is loaded with a URLClassLoader, instantiated via reflection, and its methods are invoked.

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.

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 methods to read all class names from a JAR, determine whether a class is a Spring bean, and convert class names to bean IDs.

public static Set<String> 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, the previously registered beans must be deregistered from the Spring container using the same bean name transformation.

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));
        }
    }
}

A simple test loop repeatedly attempts hot deployment until the JAR appears, then calls the appropriate deployment method.

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);
    }
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaReflectionspringDynamic LoadingHot DeploymentJAR
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.