Hot Deployment of Custom Java Interface Implementations Using Reflection and Spring

This article demonstrates how to design a simple Calculator interface, provide two implementation strategies (annotation‑based Spring beans and reflection‑based classes), and dynamically load, register, and unload user‑supplied JAR files at runtime through hot‑deployment mechanisms in a Java backend system.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Hot Deployment of Custom Java Interface Implementations Using Reflection and Spring

During development a requirement arose to allow users to upload a JAR that contains their own implementation of a predefined interface, and for the system to hot‑deploy and switch to that implementation without restarting.

Define a Simple Interface

The example uses a basic Calculator interface with two methods:

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

Simple Implementation

Two implementation styles are considered: an annotation‑based approach managed by Spring and a reflection‑based approach that does not depend on Spring. The calculate method uses the annotation style, while add uses reflection.

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

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

    /** reflection style */
    @Override
    public int add(int a, int b) {
        return new CalculatorCore().add(a, b);
    }
}

The CalculatorCore bean provides the actual addition logic:

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

Reflection‑Based Hot Deployment

Users place their JAR at a known directory. The system loads the JAR via a URLClassLoader, obtains the implementation class by its fully‑qualified name, creates an instance via reflection, and invokes the methods.

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‑managed beans, the system scans all classes in the JAR, identifies those annotated with @Component, @Service, or @Repository, and registers them dynamically into the existing 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 JAR entries, determine if a class is a Spring bean, and transform class names to bean IDs.

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

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

public static String transformName(String className) {
    String tmp = className.substring(className.lastIndexOf('.') + 1);
    return tmp.substring(0, 1).toLowerCase() + tmp.substring(1);
}

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 IDs.

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

Testing

A test class simulates the upload process by repeatedly attempting hot deployment; if the JAR is not yet present, an exception is caught and the thread sleeps before retrying.

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 source attribution and promotional links.

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.

BackendJavaReflectionHot Deploymentjar-loadingdynamic-bean-registration
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

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.