NoClassDefFoundError for classes in dependency library when using ServiceLoader in Spring Boot

  Kiến thức lập trình

I’ve been trying to implement a plugin(extension) feature for my Spring Boot application. Problem is I’m getting NoClassDefFoundError for fat JAR classes I’m loading.
I successfully loaded Plugin using ServiceLoader, but I get NoClassDefFoundError for classes in dependency library in the plugin.

Project Structure

server (spring-boot)
├── plugins/plugin-module-1.0.0.jar
├── src
├── build.gradle
├── plugin-module
│ ├── libs/vendor-crypto.jar
│ ├── build.gradle
│ ├── src

PluginLoader.java

@Component
public class PluginLoader {

    @Value("${plugins.path:./plugins}")
    private String pluginsPath;

    public List<Plugin> loadPlugins() throws Exception {
        File dir = new File(pluginsPath);
        File[] files = dir.listFiles((d, name) -> name.endsWith(".jar"));

        List<Plugin> plugins = new ArrayList<>();
        if (files != null) {
            for (File file : files) {
                URL[] urls = {file.toURI().toURL()};
                try (URLClassLoader classLoader = new URLClassLoader(urls, this.getClass().getClassLoader())) {
                    Thread.currentThread().setContextClassLoader(classLoader);

                    ServiceLoader<Plugin> loader = ServiceLoader.load(Plugin.class, classLoader);
                    for (Plugin plugin : loader) {
                        plugins.add(plugin);
                    }
                    Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
                }
            }
        }
        return plugins;
    }
}

plugin-module/build.gradle

plugins {
    id 'java'
    id 'com.github.johnrengelman.shadow' version '7.1.2'
}

group = 'com.example'
version = '1.0.0'


java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}


repositories {
    mavenCentral()
    flatDir {
        dirs("libs")
    }
}

dependencies {
    implementation project(':plugin-common')

    // Vendor
    implementation 'com.vendor.crypto:SampleCore:1.0.0'


    // test
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

jar {
    manifest {
        attributes(
                'Implementation-Title': 'Vendor Plugin',
                'Implementation-Version': version,
                'Plugin-Class': 'com.example.plugins.VendorPlugin'
        )
    }
}


tasks.named('test') {
    useJUnitPlatform()
}

task printVendor {
    doLast {
        println "Building for vendor: $vendor"
    }
}


tasks.named('build') {
    dependsOn shadowJar
}


Error Details

Caused by: java.lang.NoClassDefFoundError: com/vendor/crypto/SampleCore
    at com.vendor.crypto.core.SomeClass.someMethod(SomeClass.java:10)
    at com.vendor.plugins.VendorPlugin.someMethod(VendorPlugin.java:10)
    at com.vendor.plugins.VendorPlugin.performAction(VendorPlugin.java:20)
    at com.example.pluginloader.PluginLoader.loadPlugins(PluginLoader.java:25)
    at com.example.pluginloader.PluginLoader.loadPlugins(PluginLoader.java:15)
    ...

The plugin-module is built using the Shadow plugin to create a fat JAR. I have verified that the SampleCore.class exists in the JAR file (plugin-module) located in the plugins folder. It is originally from the dependency library fatjar.

It seems that some classes have been successfully loaded by ServiceLoader and some are not. The problem would be solved by declaring the vendor-crypto dependency inside server/build.gradle as in plugin-module, but I only want to manage that in the plugin-module project.

Let me know if any additional information is needed.

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT