Why Manual JAR Deployment Is Outdated: Embrace Dynamic Hot‑Deploy of Uploaded JARs

The article demonstrates how to replace manual JAR deployment with a dynamic hot‑deployment mechanism that loads user‑provided JARs at runtime, supporting both Spring‑managed and pure‑reflection implementations, and explains bean registration, removal, and testing procedures.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Why Manual JAR Deployment Is Outdated: Embrace Dynamic Hot‑Deploy of Uploaded JARs

Define a Simple Interface

First, a basic Calculator interface is declared with two methods: int calculate(int a, int b) and int add(int a, int b).

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

Simple Implementation

The implementation class CalculatorImpl shows two ways a user may provide the logic: an annotation‑based method ( calculate) that relies on Spring injection, and a reflection‑based method ( add) that directly creates a CalculatorCore instance.

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

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

Reflection‑Based Hot Deployment

The user uploads a JAR to a predefined directory ( jarAddress) and provides the fully‑qualified class name of the implementation. The system loads the JAR with a URLClassLoader, obtains the class, creates an instance via reflection, and invokes the 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, identifies those annotated with @Component, @Repository, or @Service, and registers them into the current Spring container using BeanDefinitionBuilder. The loading of the JAR itself uses the same URLClassLoader as the reflection approach.

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)

DeployUtils.readJarFile

iterates over a JAR, collects fully‑qualified class names, and returns them as a Set<String>. isSpringBeanClass checks whether a class is a concrete Spring component by looking for @Component, @Repository, or @Service annotations. transformName converts a class name to a bean name by lower‑casing the first character.

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

Deleting a JAR and Its Beans

When a JAR is removed or replaced, the previously registered Spring beans must be deregistered. The deletion process mirrors registration: the same URLClassLoader loads the classes, isSpringBeanClass identifies Spring beans, and defaultListableBeanFactory.removeBeanDefinition removes them.

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 loop repeatedly attempts hot deployment. If the JAR is not yet present, an exception is caught, printed, and the thread sleeps for 10 seconds, allowing the developer to copy the JAR into the target directory manually.

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 LoadingHot DeploymentJar
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow 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.