0

I created a common library to use it in my spring microservices. In this library I used some other libraries (example: libphonenumber). I created the jar file and imported it into another project, and I used it without any problem. But when building the project, this exception is thrown :

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'phoneUtils': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [com.weryou.backend.commons.utils.PhoneUtils] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@368239c8]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:265) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1269) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1184) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:743) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:390) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1214) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1203) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at com.weryou.backend.mission.MissionAppKt.main(MissionApp.kt:13) ~[main/:na]
Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.weryou.backend.commons.utils.PhoneUtils] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@368239c8]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:507) ~[spring-core-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:404) ~[spring-core-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:389) ~[spring-core-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:248) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
... 18 common frames omitted
Caused by: java.lang.NoClassDefFoundError: com/google/i18n/phonenumbers/Phonenumber$PhoneNumber
at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3167) ~[na:na]
at java.base/java.lang.Class.getDeclaredMethods(Class.java:2310) ~[na:na]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:489) ~[spring-core-5.1.9.RELEASE.jar:5.1.9.RELEASE]
... 21 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.google.i18n.phonenumbers.Phonenumber$PhoneNumber
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
... 25 common frames omitted

I do not know why this exception is thrown from the project where I imported the library .. normally if there is a problem at the dependencies of the library, the library does not work, but it works. But when i put in the project gradle the same dependencies used in the library, everything works fine. normally if I used dependencies in a library, I do not need to implement them in the project that the library uses. I tried to make the dependencies of the library transitive, but that does not works!

this is the gradle file of the library :

plugins {
kotlin("jvm") version "1.3.50"
}

group = "com.weryou.backend"
version = "1.0.0"
java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    implementation("io.jsonwebtoken:jjwt:0.9.1") {
        isTransitive = true
    }
    implementation("com.googlecode.libphonenumber:libphonenumber:8.10.14") {
        isTransitive = true
    }
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9") {
        isTransitive = true
    }
}

and this is the gradle file of the project that use the library

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.1.7.RELEASE"
    id("io.spring.dependency-management") version "1.0.8.RELEASE"
    kotlin("jvm") version "1.2.71"
    kotlin("plugin.spring") version "1.2.71"
}

group = "com.project.test"
version = "0.0.1"
java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
   mavenCentral()
}

dependencies {
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))

    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

    implementation("org.springframework.boot:spring-boot-starter-web")
}

dependencyManagement {
    imports {
        mavenBom("org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR2")
    }
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "1.8"
    }
}

1 Answers1

0

If you look at your consuming project, you declare a dependency to your common library like this

dependencies {
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
    // ...
}

A jar file in itself does not contain any information on all the transitive dependencies it needs*. So there is no way that your consuming project would know that just by looking at the jar file.

There are several ways you can do this, and it all depends on your layout and how tightly coupled you like the projects to be. Here are a few of them:

  1. In the consuming project, declare all required dependencies for the library (again). This is what you have already tried and said works fine. Hopefully now you know why. The downside is that you will have to keep them synchronized manually.

  2. Create a Maven distribution of your library and have your consuming project depend on it like you do for all the other dependencies. This requires you to have a Maven repository to be able to upload it to. In this approach, the transitive dependencies will be registered in the corresponding POM file.

  3. If the projects are in on the same machine, you can import the library as a composite build, and declare it as a project dependency. In this approach, Gradle will infer the transitive dependencies from the Gradle project itself.

(*) Unless you are using a manifest file or building it as a "fat" jar, which I assume you don't and won't recommend anyway.

Bjørn Vester
  • 6,851
  • 1
  • 20
  • 20