Stop Manually Deploying JARs: Embrace Dynamic Hot Deployment

The article demonstrates how to let users upload a JAR that implements a given interface, then hot‑replace the implementation at runtime using either reflection or Spring annotation‑based loading, covering class loading, bean registration, and cleanup with concrete code examples.

Top Architect
Top Architect
Top Architect
Stop Manually Deploying JARs: Embrace Dynamic Hot Deployment

During recent development a requirement arose to let users provide their own implementation of a predefined interface, package it as a JAR, upload it to the system, and have the system hot‑deploy the new implementation without restarting.

Define a simple interface

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

Implementation class (annotation and reflection modes)

The implementation uses Spring annotation for calculate and plain reflection for add:

@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 bean is used to verify that, in annotation mode, the system can fully construct the bean dependency graph and register it in the current Spring container.

@Service
public class CalculatorCore {
    public int add(int a, int b) {
        return a + b;
    }
}

Reflection‑based hot deployment

Users specify the JAR file path ( jarAddress) and its URL ( jarPath). The system 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.

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

Annotation‑based hot deployment

If the uploaded JAR contains Spring components, the system scans all classes in the JAR, detects those annotated with @Component, @Repository or @Service, and registers each as a bean in the existing Spring context.

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 (DeployUtils)

Read all class files from a JAR :

public static Set<String> readJarFile(String jarAddress) throws IOException {
    Set<String> classNameSet = new HashSet<>();
    JarFile jarFile = new JarFile(jarAddress);
    Enumeration<JarEntry> 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 whether 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;
}

Transform 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 a JAR

When a JAR is deleted or replaced, the beans that were registered from it must be removed from the Spring container using the same context:

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 test class simulates the user uploading the JAR. It repeatedly attempts hot deployment, sleeping ten seconds after a failure to allow the JAR to be placed in the directory.

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 therefore provides a complete, step‑by‑step guide to dynamically loading, registering, invoking, and cleaning up user‑provided JAR implementations in a Java/Spring backend.

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
Top Architect
Written by

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.

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.