Backend Development 9 min read

Hot‑Deployable Java Interface Implementations Using Jar Upload, Reflection, and Spring

This article demonstrates how to let users upload a JAR containing a custom implementation of a predefined Java interface, then hot‑deploy the new implementation at runtime using either reflection or Spring bean registration, with complete code examples and utility methods.

Architect
Architect
Architect
Hot‑Deployable Java Interface Implementations Using Jar Upload, Reflection, and Spring

In a recent development scenario a system needs to accept a user‑provided JAR that implements a predefined interface, load the JAR at runtime, and hot‑replace the current implementation without restarting the application.

The example starts with a simple Calculator interface that declares two methods:

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

A concrete implementation CalculatorImpl is shown, which injects a CalculatorCore bean and provides both the Spring‑managed calculate method and the reflection‑based add method:

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

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

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

The supporting CalculatorCore class simply adds two integers:

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

Two deployment strategies are described. The reflection approach loads the uploaded JAR with a URLClassLoader , obtains the implementation class by its fully‑qualified name, creates an instance via newInstance() , and invokes the desired method:

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

The Spring‑based hot deployment scans the JAR for all class files, loads each class, checks whether it carries a Spring stereotype annotation ( @Component , @Service , @Repository ), and registers qualifying classes as beans in the existing ApplicationContext :

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 used by the Spring deployment are provided:

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 tmp = className.substring(className.lastIndexOf(".") + 1);
    return tmp.substring(0, 1).toLowerCase() + tmp.substring(1);
}

When a JAR is removed, the previously registered Spring beans must also be deregistered. The following method iterates over the classes in the JAR and removes any bean definitions that were added earlier:

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

A simple test harness creates a ClassPathXmlApplicationContext , obtains the DefaultListableBeanFactory , and repeatedly attempts hot deployment (either reflection or Spring) while sleeping for ten seconds after any exception, allowing the developer to drop a new JAR into the monitored directory during the wait:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
while (true) {
    try {
        hotDeployWithReflect(); // or hotDeployWithSpring(); or delete();
    } catch (Exception e) {
        e.printStackTrace();
        Thread.sleep(1000 * 10);
    }
}

The article concludes with a reminder that the hot‑deployment process for Spring‑managed beans is essentially a class‑loading plus dynamic registration workflow, and that bean removal must be performed using the same application context to keep the container consistent.

backendJavareflectionSpringHot Deploymentjar-loading
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

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.