How to Hot-Load JAR Plugins in Spring Boot with Dynamic Bean Registration

This article explains how to dynamically load JAR plugins in a Spring Boot application using URLClassLoader, share the class loader with the main context, and register plugin classes as Spring beans both at startup and at runtime, enabling seamless extension without server restarts.

Cognitive Technology Team
Cognitive Technology Team
Cognitive Technology Team
How to Hot-Load JAR Plugins in Spring Boot with Dynamic Bean Registration

Background

Dynamic plugin programming decouples business functionality, improves maintainability, enhances extensibility, and allows features to be added without restarting the server. Traditional solutions like SPI or OSGi do not integrate plugins with Spring IoC, preventing bean injection from the main application.

This article introduces a method to hot‑load JAR files in a Spring Boot project, register them as beans, and inject main‑application beans into plugins for richer functionality.

Hot Loading JAR Files

By specifying a URL or file path, a JAR can be loaded at runtime using URLClassLoader and its addURL method. Example implementation:

public class ClassLoaderUtil {
    public static ClassLoader getClassLoader(String url) {
        try {
            Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
            URLClassLoader classLoader = new URLClassLoader(new URL[]{}, ClassLoader.getSystemClassLoader());
            method.invoke(classLoader, new URL(url));
            return classLoader;
        } catch (Exception e) {
            log.error("getClassLoader-error", e);
            return null;
        }
    }
}

When creating the URLClassLoader, the system class loader must be set as its parent to bridge the main application and plugin class loaders, avoiding ClassNotFoundException during bean registration.

Dynamic Bean Registration

Plugin classes loaded from the JAR can be registered into Spring's IoC container, and existing beans from the main application can be injected into the plugin. This can be done at application startup or during runtime.

Startup Bean Registration

Use ImportBeanDefinitionRegistrar to register plugin beans when Spring Boot starts:

public class PluginImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    private final String targetUrl = "file:/D:/SpringBootPluginTest/plugins/plugin-impl-0.0.1-SNAPSHOT.jar";
    private final String pluginClass = "com.plugin.impl.PluginImpl";

    @SneakyThrows
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ClassLoader classLoader = ClassLoaderUtil.getClassLoader(targetUrl);
        Class<?> clazz = classLoader.loadClass(pluginClass);
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        BeanDefinition beanDefinition = builder.getBeanDefinition();
        registry.registerBeanDefinition(clazz.getName(), beanDefinition);
    }
}

Runtime Bean Registration

Use the ApplicationContext to register beans while the application is running:

@GetMapping("/reload")
public Object reload() throws ClassNotFoundException {
    ClassLoader classLoader = ClassLoaderUtil.getClassLoader(targetUrl);
    Class<?> clazz = classLoader.loadClass(pluginClass);
    springUtil.registerBean(clazz.getName(), clazz);
    PluginInterface plugin = (PluginInterface) springUtil.getBean(clazz.getName());
    return plugin.sayHello("test reload");
}

The helper SpringUtil implements ApplicationContextAware to expose registration and retrieval methods:

@Component
public class SpringUtil implements ApplicationContextAware {
    private DefaultListableBeanFactory defaultListableBeanFactory;
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        this.defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
    }

    public void registerBean(String beanName, Class<?> clazz) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());
    }

    public Object getBean(String name) {
        return applicationContext.getBean(name);
    }
}

Summary

The approach shares a ClassLoader and dynamically registers beans, opening communication between plugins and the main program. This enables plugins to inject main‑application beans such as Redis or DataSource and call remote services.

However, because plugins share the same ClassLoader, class and version conflicts may arise, and loaded classes cannot be unloaded unless their names or paths change.

Therefore, this solution is best suited for scenarios with a small number of well‑tested plugins, clear development standards, and where plugins are packaged and released after thorough testing.

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.

Spring Boothot-reloadDynamic PluginsBean Registration
Cognitive Technology Team
Written by

Cognitive Technology Team

Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.

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.