Backend Development 20 min read

Understanding Java SPI and Dubbo Extension Loading Mechanism

The article explains Java’s built‑in SPI mechanism, then details Dubbo’s enhanced SPI—including ExtensionLoader caching, wrapper and adaptive classes, and @Activate automatic activation—showing how extensions are discovered from META‑INF directories, classified, and loaded on demand for flexible, plug‑in style service implementation.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
Understanding Java SPI and Dubbo Extension Loading Mechanism

SPI (Service Provider Interface) is a service discovery mechanism that follows a plug‑in style: an interface is defined, and different implementations can be provided without the caller needing to know the concrete class. In Java, SPI embodies interface‑oriented programming and satisfies the Open‑Closed Principle.

1. JDK built‑in SPI

Since JDK 1.6, many components use SPI, the most common example being JDBC drivers where only java.sql.Driver is defined in the JDK and each vendor supplies an implementation.

Below is a minimal example that demonstrates how to use Java SPI:

package com.vivo.study;
public interface Car {
    void getPrice();
}
package com.vivo.study.impl;
/**
 * Implementation 1
 */
public class AudiCar implements Car {
    @Override
    public void getPrice() {
        System.out.println("Audi A6L's price is  500000 RMB.");
    }
}

package com.vivo.study.impl;
/**
 * Implementation 2
 */
public class BydCar implements Car {
    @Override
    public void getPrice() {
        System.out.println("BYD han's price is 220000 RMB.");
    }
}

The service file META-INF/services/com.vivo.study.Car contains the fully qualified class names of the implementations:

com.vivo.study.impl.AudiCar
com.vivo.study.impl.BydCar
public class SpiDemo {
    public static void main(String[] args) {
        ServiceLoader<Car> load = ServiceLoader.load(Car.class);
        for (Car car : load) {
            car.getPrice();
        }
    }
}

The JDK ServiceLoader loads all implementations eagerly, which can be a drawback when only a specific implementation is needed.

2. Dubbo SPI

Dubbo extends the JDK SPI mechanism and adds several improvements:

More flexible configuration (key:value pairs) allowing precise, on‑demand loading.

Caching of loaded classes to avoid repeated instantiation.

Support for wrapper classes, adaptive classes, and automatic activation.

Dubbo looks for extension definition files in three locations:

META-INF/dubbo/internal/
META-INF/dubbo/
META-INF/services/

For example, the interface org.apache.dubbo.common.extension.LoadingStrategy is placed under META-INF/services/ , but most projects use Dubbo’s own optimized SPI implementation.

2.1 ExtensionLoader

The core class ExtensionLoader plays the same role for Dubbo as ServiceLoader does for the JDK. It is responsible for locating, loading, and caching extension classes.

public class ExtensionLoader
{
    // ConcurrentHashMap cache, key -> Class, value -> ExtensionLoader instance
    private static final ConcurrentMap
, ExtensionLoader
> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);

    private ExtensionLoader(Class
type) { /* ... */ }

    public static
ExtensionLoader
getExtensionLoader(Class
type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }
        ExtensionLoader
loader = (ExtensionLoader
) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader
(type));
            loader = (ExtensionLoader
) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }
    // ... other methods omitted for brevity ...
}

When an interface is annotated with @SPI , each such interface gets a single ExtensionLoader instance, which is cached for the lifetime of the application.

2.2 Loading Process

The loading process starts with getExtensionClasses() , which first checks the cache and then calls loadExtensionClasses() if needed. The steps can be visualised as:

getExtensionClasses
  └─>loadExtensionClasses
        ├─>cacheDefaultExtensionName
        ├─>loadDirectory
        │     └─>loadResource
        │           └─>loadClass

During this process, Dubbo reads the extension files from the three META‑INF directories, classifies the classes (adaptive, wrapper, activate, ordinary) and stores them in appropriate caches such as cachedAdaptiveClass , cachedActivates , and cachedWrapperClasses .

2.3 Wrapper Classes

A wrapper class is an implementation that has a constructor accepting the same interface type. It allows the original implementation to be wrapped layer by layer, similar to AOP. For example, ProtocolFilterWrapper and ProtocolListenerWrapper are wrapper classes for the Protocol extension point.

2.4 Adaptive Extension Classes

The @Adaptive annotation marks either a class or a method as adaptive. When no adaptive class is provided, Dubbo generates one at runtime (e.g., SimpleExt$Adaptive ) using Javassist. The generated class determines the concrete extension name from URL parameters and delegates the call.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    String[] value() default {};
}

Example of an adaptive interface:

@SPI("impl1")
public interface SimpleExt {
    @Adaptive
    String echo(URL url, String s);

    @Adaptive({"key1", "key2"})
    String yell(URL url, String s);

    String bang(URL url, int i);
}

The generated adaptive class contains logic such as:

public String yell(org.apache.dubbo.common.URL arg0, java.lang.String arg1) {
    if (arg0 == null) throw new IllegalArgumentException("url == null");
    org.apache.dubbo.common.URL url = arg0;
    String extName = url.getParameter("key1", url.getParameter("key2", "impl1"));
    if (extName == null)
        throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.ext1.SimpleExt) name from url (" + url.toString() + ") use keys([key1, key2])");
    SimpleExt extension = (SimpleExt)ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension(extName);
    return extension.yell(arg0, arg1);
}

Adaptive methods allow the concrete implementation to be chosen based on URL parameters, providing great flexibility.

2.5 Automatic Activation (@Activate)

The @Activate annotation marks an extension to be automatically loaded when certain conditions are met (e.g., specific keys in the URL). Dubbo retrieves such extensions via ExtensionLoader.getActivateExtension(URL, String, String) , which is heavily used in filter chains during service export and reference.

// Example of obtaining activated filters
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), "service.filter", "provider");

These activated extensions enable features such as logging, metrics, and security without manual configuration.

3. Summary

Dubbo SPI builds on JDK SPI and adds precise, on‑demand loading, caching, and richer extension types.

ExtensionLoader reads extension definitions from three META‑INF directories, classifies them, and caches the results.

Wrapper classes provide AOP‑like capabilities for extensions.

Adaptive classes are generated at runtime when not supplied, allowing URL‑driven selection of implementations.

@Activate enables automatic activation of extensions based on configurable conditions.

JavaBackend DevelopmentDubboSPIAdaptiveextensionloader
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

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.