Backend Development 10 min read

Hot Deployment of Java Interface Implementations Using Jar Packages with Spring and Reflection

This article demonstrates how to design a simple Calculator interface, provide two implementation strategies (Spring annotation and reflection), and achieve hot deployment by uploading JAR files, dynamically loading classes, registering or removing beans in the Spring container, and testing the runtime behavior.

Top Architect
Top Architect
Top Architect
Hot Deployment of Java Interface Implementations Using Jar Packages with Spring and Reflection

During recent development a requirement arose to allow users to upload a JAR containing an implementation of a predefined interface, which the system should hot‑deploy and switch to at runtime.

Define a Simple Interface

We start with a basic Calculator interface:

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. The calculate method uses the annotation mode, while add uses reflection.

@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 supporting 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

Users upload a JAR to a predefined 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 its 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, identifies those annotated with @Component , @Service , or @Repository , and registers them into the current Spring context dynamically.

public static void hotDeployWithSpring() throws Exception {
    Set
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.readJarFile reads all class entries from a JAR and returns their fully‑qualified names. isSpringBeanClass checks whether a class carries Spring stereotypes. transformName converts a class name to a bean name with a lowercase first letter.

public static Set
readJarFile(String jarAddress) throws IOException {
    Set
classNameSet = new HashSet<>();
    JarFile jarFile = new JarFile(jarAddress);
    Enumeration
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;
    }
    if (cla.getAnnotation(Component.class) != null) return true;
    if (cla.getAnnotation(Repository.class) != null) return true;
    if (cla.getAnnotation(Service.class) != null) return true;
    return false;
}

public static String transformName(String className) {
    String tmpstr = className.substring(className.lastIndexOf(".") + 1);
    return tmpstr.substring(0, 1).toLowerCase() + tmpstr.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 names:

public static void delete() throws Exception {
    Set
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. It repeatedly attempts hot deployment, sleeping for 10 seconds when the JAR is not yet present, allowing manual placement of the JAR for verification.

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 by inviting readers to discuss the approach, share questions, and join a community of architects for further exchange.

JavareflectionSpringHot Deploymentjar-loadingDynamic Bean Registration
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

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