Stop Manually Deploying JARs—Dynamic Hot Deployment Made Easy

This article shows how to let users upload a JAR that implements a predefined interface, then hot‑deploy the new implementation at runtime using either Spring annotation‑based registration or plain reflection, including bean registration, removal, and a test harness.

java1234
java1234
java1234
Stop Manually Deploying JARs—Dynamic Hot Deployment Made Easy

During development a system may expose an interface that users can implement, package into a JAR, upload to the server, 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)

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

CalculatorCore used by the implementation

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

Reflection‑based hot deployment

The uploaded JAR is placed at jarAddress and its URL is built as jarPath. The system loads the JAR with a URLClassLoader, obtains the implementation class by its fully‑qualified name, creates an instance, and invokes the 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);
}

Annotation‑based hot deployment

If the uploaded JAR contains Spring components, the system scans all classes, detects those annotated with @Component, @Repository or @Service, and registers them 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());
        }
    }
}

Utility class DeployUtils

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

Removing beans when a JAR is deleted

When a JAR is removed or replaced, the previously registered beans must be deregistered from the Spring container using the same class‑loading logic.

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 simple test class creates the Spring context, obtains the bean factory, and repeatedly attempts hot deployment, sleeping when the JAR is not yet present.

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 JAR LoadingHot DeploymentBean Registration
java1234
Written by

java1234

Former senior programmer at a Fortune Global 500 company, dedicated to sharing Java expertise. Visit Feng's site: Java Knowledge Sharing, www.java1234.com

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.