简体   繁体   中英

Spring boot: Inclusion of external jar dependency with scanBasePackages

I'll first describe the problem, files referenced here can be found below.


I am trying to use an external library in the form of a jar file. This library contains code that I use in my Spring @Configuration file. As such I have included it as an implementation dependency in my build.gradle (see code below).

All of the library files are contained in the package com.rusticisoftware.tincan . My own code resides under de.my.example .

Leaving the @SpringBootApplication at its default (which is the same as de.my.example as this is where the Application.java class resides works when assembling the project with the Gradle assemble task. I can then start the project from my IDE and everything works as expected.

However, both when running the Gradle test task and when executing the bootJar created by spring boot's bootJar task the following error is thrown:

    java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
        Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:798
            Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:656
                Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:185
                    Caused by: java.lang.NoClassDefFoundError at Config.java:29
                        Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:581

Where Caused by: java.lang.NoClassDefFoundError at Config.java:29 is referencing this line in the code: RemoteLRS lrs = new RemoteLRS(); RemoteLRS is the class from the jar-library contained in com.rusticisoftware.tincan .

I was able to rid myself of this error by specifiying the scanBasePackages in the @SpringBootApplication annotation like so: @SpringBootApplication(scanBasePackages = {"com.rusticisoftware.tincan"}) . Doing this will however then no longer scan and find MyResource.java leading the REST service to not load.

To fix this issue, I tried manually adding my own package to @SpringBootApplication together with the package from the jar, leading to this: @SpringBootApplication(scanBasePackages = {"com.rusticisoftware.tincan", "de.my.example"}) .

This once again loads the REST service, but runs into the same problem as before when running the test task or executing the jar generated by the bootJar task. I have also manually inspected the generated bootJar and can confirm that the library jar is included within.


Project structure

Project
 |
 +-- build.gradle
 |    
 +-- src
 |    |
 |    +-- main/java/
 |    |        |  
 |    |        +-- de/my/example
 |    |                   |
 |    |                   +-- Application.java
 |    |                   |
 |    |                   +-- Config.java
 |    |                   |
 |    |                   +-- controller
 |    |                           |
 |    |                           +-- MyResource.java
 |    +-- test/java/
 |             |  
 |             +-- de/my/example
 |                        |
 |                        +-- ApplicationTests.java
 +-- libs
      |
      +-- tincan-1.1.0.jar

build.gradle

plugins {
    id "java"
    id "idea"
    id 'org.springframework.boot' version '2.3.0.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
}

// project settings
group 'de.my.example'
version '1.0'
sourceCompatibility = 11

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    // spring
    implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }

    // tin-can xAPI
    implementation files('libs/tincan-1.1.0.jar')
}

test {
    useJUnitPlatform()
}

Application.java

@SpringBootApplication(scanBasePackages = {"com.rusticisoftware.tincan", "de.my.example"})
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

ApplicationTests.java

@SpringBootTest
class ApplicationTests {

    @Test
    void contextLoads() {
    }

}

TestResource.java

public class TestResource {

    private final LearningRecordStore learningRecordStore;

    public TestResource(LearningRecordStore learningRecordStore) {
        this.learningRecordStore = learningRecordStore;
    }

    @GetMapping(value = "/ping", produces = MediaType.TEXT_PLAIN_VALUE)
    public String ping() {
        return "pong";
    }
}

Config.java

@Configuration
public class Config {

    @Bean
    public LearningRecordStore learningData() {
        RemoteLRS lrs = new RemoteLRS();
        return new LearningRecordStore(lrs);
    }

}

The simplest way to check is to explore build file after Gradle build to ensure it's present in build package. This way, you will make sure, it is getting included and should not throw class-not-found exception. Also, you might need to include both the packages in the scanBasePackage.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM