Backend Development 17 min read

Dynamic Loading and Unloading of Governance Tasks in Java Using a Custom ClassLoader and XXL‑Job

This article explains how to design a plug‑in style architecture for data‑governance tasks by creating a custom URLClassLoader, dynamically loading JARs into a Spring context, registering and deregistering XXL‑Job handlers, updating configuration via YAML or Nacos, and finally unloading resources without restarting the service.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Dynamic Loading and Unloading of Governance Tasks in Java Using a Custom ClassLoader and XXL‑Job

Overview

The data‑governance service contains many tasks; modifying or adding a task currently requires a full service restart, which disrupts other tasks. The goal is to enable dynamic start/stop, upgrade, and addition of tasks without affecting running tasks.

Solution

Load business functions dynamically using a custom class loader to achieve plug‑in loading and composable deployment.

Register each governance task as an XXL‑Job task so that they can be managed uniformly.

Dynamic Loading

1. Custom Class Loader

A MyClassLoader extends URLClassLoader and keeps a map of loaded classes for easy unloading. It overrides findClass to first check the map, then delegate to the parent loader, and provides an unload method that clears resources via reflection.

package cn.jy.sjzl.util;

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Custom class loader
 */
public class MyClassLoader extends URLClassLoader {
    private Map
> loadedClasses = new ConcurrentHashMap<>();
    public Map
> getLoadedClasses() { return loadedClasses; }
    public MyClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); }
    @Override
    protected Class
findClass(String name) throws ClassNotFoundException {
        Class
clazz = loadedClasses.get(name);
        if (clazz != null) { return clazz; }
        try {
            clazz = super.findClass(name);
            loadedClasses.put(name, clazz);
            return clazz;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
    public void unload() {
        try {
            for (Map.Entry
> entry : loadedClasses.entrySet()) {
                String className = entry.getKey();
                loadedClasses.remove(className);
                try {
                    Method destroy = entry.getValue().getDeclaredMethod("destory");
                    destroy.invoke(entry.getValue());
                } catch (Exception ignored) {}
            }
            close();
        } catch (Exception e) { e.printStackTrace(); }
    }
}

2. Dynamic Loading Process

The DynamicLoad component reads a JAR file, creates a MyClassLoader , loads all classes, registers Spring‑annotated beans into the Spring container, and registers methods annotated with @XxlJob as XXL‑Job handlers.

package com.jy.dynamicLoad;

import com.jy.annotation.XxlJobCron;
import com.jy.classLoader.MyClassLoader;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.core.handler.impl.MethodJobHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Dynamic loading component
 */
@Component
public class DynamicLoad {
    private static Logger logger = LoggerFactory.getLogger(DynamicLoad.class);
    @Autowired
    private ApplicationContext applicationContext;
    private Map
myClassLoaderCenter = new ConcurrentHashMap<>();
    @Value("${dynamicLoad.path}")
    private String path;

    /**
     * Load a specific JAR at runtime
     */
    public void loadJar(String path, String fileName, Boolean isRegistXxlJob) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        File file = new File(path + "/" + fileName);
        // ... (omitted for brevity, see source for full implementation)
    }
}

3. Dynamic Unloading

Unloading reverses the loading steps: it removes the job handler from the XXL‑Job executor, destroys the bean from the Spring container, clears the class map in the custom loader, and finally calls unload() on the loader.

public void unloadJar(String fileName) throws IllegalAccessException, NoSuchFieldException {
    MyClassLoader myClassLoader = myClassLoaderCenter.get(fileName);
    // Remove job handlers from XXL‑Job executor
    // Destroy Spring beans
    // Remove classes from class loader internals via reflection
    // Finally unload the custom loader
}

Dynamic Configuration

To keep the system state after a restart, the loaded JAR list is persisted either by modifying a local bootstrap.yml (using SnakeYAML) or by updating a Nacos configuration file.

org.yaml
snakeyaml
1.29

Configuration utilities ( ConfigUpdater , NacosConfigUtil ) read, modify, and publish the updated YAML or Nacos data so that newly loaded tasks are remembered.

Packaging

When separating the dynamic modules, the Maven shade plugin can be configured in pom.xml to produce a shaded JAR that contains only the required packages.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.4</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals><goal>shade</goal></goals>
                    <configuration>
                        <filters>
                            <filter>
                                <artifact>*:*</artifact>
                                <includes>
                                    <include>com/jy/job/demo/**</include>
                                </includes>
                            </filter>
                        </filters>
                        <finalName>demoJob</finalName>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

With these mechanisms, governance tasks can be added, upgraded, or removed on‑the‑fly, eliminating service downtime.

Javaconfiguration managementSpringxxl-jobCustom ClassLoaderDynamic Class Loading
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

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.