Backend Development 10 min read

Designing an Extension Plugin System (EXP) for Enterprise Application Customization

This article examines the challenges of customizing B2B software, critiques branch‑based development, and proposes a plugin architecture with extensible interfaces, hot‑plug support, tenant isolation, and Spring integration, providing Java code examples and implementation guidelines.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Designing an Extension Plugin System (EXP) for Enterprise Application Customization

For many years B2B applications have struggled with how to handle heavy customization requests, especially from large customers who demand non‑standard databases, extra security checks, or custom fields. Traditional solutions include embedding the feature into the core product, creating a time‑critical branch, or developing a separate branch, each with its own drawbacks.

Branch development is fast but leads to massive upgrade costs because merging large, divergent changes becomes difficult. Unlike SaaS, on‑premise software often requires manual upgrades, and conflicting code bases make merges painful.

The article proposes using a plugin mechanism as a solution: the main program defines extension points, and custom logic is implemented in isolated plugins, similar to a sophisticated Strategy pattern or SPI with class‑loader isolation.

Design requirements for the plugin system include:

The core program defines empty interfaces that plugins implement, allowing optional installation or removal.

Hot‑plug capability to avoid restarts.

Support for servlet or Spring REST APIs, transactions, MyBatis, AOP, RPC, etc.

Multiple implementations per extension point with routing strategies for different customers.

Separate, hot‑updatable configuration APIs distinct from the main application.yml .

Support for both JAR and ZIP plugin packages.

Class‑loader isolation to prevent dependency conflicts between plugins.

The core API is defined by the ExpAppContext interface:

public interface ExpAppContext {
    /** Load a plugin */
    Plugin load(File file) throws Throwable;

    /** Unload a plugin */
    void unload(String id) throws Exception;

    /** Get multiple plugin instances for an extension point */
List
get(String extCode);

    /** Simplified get using full class name */
List
get(Class
pClass);

    /** Get a single plugin instance */
P get(String extCode, String pluginId);
}

Tenant‑specific behavior is handled via TenantService and TenantCallback interfaces, allowing custom sorting and ownership checks per tenant.

public interface TenantService {
    /** Get TenantCallback implementation */
    default TenantCallback getTenantCallback() {
        return TenantCallback.TenantCallbackMock.instance;
    }

    /** Set the callback */
    default void setTenantCallback(TenantCallback callback) {}
}

public interface TenantCallback {
    /** Return plugin sort order (default 0) */
    Integer getSort(String pluginId);

    /** Determine if the plugin belongs to the current tenant */
    Boolean isOwnCurrentTenant(String pluginId);
}

Example usage shows bootstrapping the plugin system, setting a tenant callback, and retrieving the highest‑priority implementation of a UserService extension point:

public static void main(String[] args) throws Throwable {
    Class
extensionClass = UserService.class;
    ExpAppContext expAppContext = Bootstrap.bootstrap("exp-plugins/", "workdir-simple-java-app");
    expAppContext.setTenantCallback(new TenantCallback() {
        @Override
        public Integer getSort(String pluginId) {
            return new Random().nextInt(10);
        }
        @Override
        public Boolean isOwnCurrentTenant(String pluginId) {
            return true;
        }
    });
    Optional
first = expAppContext.get(extensionClass).stream().findFirst();
    first.ifPresent(userService -> {
        System.out.println(userService.getClass());
        System.out.println(userService.getClass().getClassLoader());
        userService.createUserExt();
    });
}

Plugin configuration is accessed via the PluginConfig API, which isolates properties by plugin ID (and optionally by tenant):

public interface PluginConfig {
    String getProperty(String pluginId, String key, String defaultValue);
}

Implementation details include class‑loader mechanisms for JAR/ZIP isolation, Spring container injection of plugin beans, and full hot‑plug support for loading, unloading, and removing plugins from the Spring context.

The full source code, examples, Spring Boot starter integration, and best‑practice guidelines are available in the GitHub repository stateIs0/exp . The project aims to help developers address customization challenges in localized enterprise software.

In summary, EXP (Extension Plugin) is a plugin‑based extension point system designed to simplify custom feature development, improve upgradeability, and support multi‑tenant isolation for on‑premise applications.

Javasoftware architectureBackend DevelopmentSpringPlugin System
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.