Spring 来自终端的 Keycloak 运行 引导/安全类加载器问题

Spring Boot / Security classloader issues with Keycloak run from terminal

我将 Spring 启动和 Spring 安全与 Keycloak 结合使用。构建工具是 gradle.

当我 运行 ./gradlew bootRun 时,应用程序运行完美。如果我使用生成的 fat jar(即 java -jar myapp.jar),应用程序将启动,但当应用程序尝试调用一些 keyloak 东西时我遇到异常:

java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource referenced from a method is not visible from class loader
    at java.base/java.lang.reflect.Proxy$ProxyBuilder.ensureVisible(Proxy.java:851) ~[na:na]
    at java.base/java.lang.reflect.Proxy$ProxyBuilder.validateProxyInterfaces(Proxy.java:682) ~[na:na]
    at java.base/java.lang.reflect.Proxy$ProxyBuilder.<init>(Proxy.java:628) ~[na:na]
    at java.base/java.lang.reflect.Proxy.lambda$getProxyConstructor(Proxy.java:426) ~[na:na]
    at java.base/jdk.internal.loader.AbstractClassLoaderValue$Memoizer.get(AbstractClassLoaderValue.java:327) ~[na:na]
    at java.base/jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(AbstractClassLoaderValue.java:203) ~[na:na]
    at java.base/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:424) ~[na:na]
    at java.base/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:999) ~[na:na]
    at org.jboss.resteasy.client.jaxrs.ProxyBuilder.proxy(ProxyBuilder.java:79) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
    at org.jboss.resteasy.client.jaxrs.ProxyBuilder.build(ProxyBuilder.java:131) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
    at org.jboss.resteasy.client.jaxrs.internal.ClientWebTarget.proxy(ClientWebTarget.java:93) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
    at org.keycloak.admin.client.Keycloak.realms(Keycloak.java:114) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]
    at org.keycloak.admin.client.Keycloak.realm(Keycloak.java:118) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]

所以我发现我在终端中启动应用程序的方式一定有问题。我找到了 this official site,它解释了如何从终端 运行 一个 Spring 应用程序。所以我尝试了所有的解决方案,包括:

$ unzip -q myapp.jar
$ java org.springframework.boot.loader.JarLauncher

但我得到了同样的错误。

经过 2 天的搜索和试验,我没有任何想法。

所以我的问题基本上是: 有人知道如何解决这个问题吗?

我对类加载器的了解也有限 - 因此也非常欢迎这方面的任何实用提示。

编辑(添加 build.gradle): Jackson 存在一些问题,这就是为什么直接包含 Spring 网络依赖项(没有启动器,因此没有 Jackson)。 这是 build.gradle:

buildscript {
   ext {
      kotlinVersion = '1.2.20'
      springBootVersion = '1.5.7.RELEASE'
      keycloakVersion = '3.4.3.Final'
      restEasyClientVersion = '3.1.4.Final'
      postgresServerVersion = '10.0'
      postgresJdbcDriverVersion = '42.1.4'
      spekVersion = '1.1.5'
      jacksonVersion = '2.8.10'
      javaxWsRsVersion = '2.1'

      logbackVersion = '1.2.3'
      slf4jVersion = '1.7.25'
   }
   repositories {
      mavenCentral()
      jcenter()
      //spring dev
      maven { url 'https://repo.spring.io/snapshot' }
      maven { url 'https://repo.spring.io/milestone' }
      //Kotlin dev
      maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
      //gradle plugins
      maven { url "https://plugins.gradle.org/m2/" }
   }
   dependencies {
      classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
      classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
      classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
      classpath('com.bmuschko:gradle-docker-plugin:3.2.0')

      //for tests
      classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0'
   }
}

allprojects {

   repositories {
      mavenCentral()
      jcenter()
      //spring dev
      maven { url 'https://repo.spring.io/snapshot' }
      maven { url 'https://repo.spring.io/milestone' }
   }

}

subprojects {

   repositories {
      maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
      // for tests
      maven { url "http://dl.bintray.com/jetbrains/spek" }
   }

   // for kotlin
   apply plugin: 'kotlin'
   // for tests
   apply plugin: 'org.junit.platform.gradle.plugin'

   apply plugin: 'kotlin-spring'
   apply plugin: 'org.springframework.boot'
   apply plugin: 'io.spring.dependency-management'

   // for tests
   junitPlatform {
      filters {
         engines {
            include 'spek'
         }
      }
   }

   sourceCompatibility = 1.9

   dependencies {
      // kotlin
      compile("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
      compile("org.jetbrains.kotlin:kotlin-test:${kotlinVersion}")
      compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
      // jackson
      compile "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}"
      compile "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
      compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:${jacksonVersion}"
      compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:${jacksonVersion}"
      compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.2"

      // Java WS RS
      compile "javax.ws.rs:javax.ws.rs-api:${javaxWsRsVersion}"
      // for tests
      testCompile "org.jetbrains.spek:spek-api:${spekVersion}"
      testRuntime "org.jetbrains.spek:spek-junit-platform-engine:${spekVersion}"
      testCompile ("org.jetbrains.spek:spek-api:${spekVersion}") {
         exclude group: 'org.jetbrains.kotlin'
      }
      testRuntime ("org.jetbrains.spek:spek-junit-platform-engine:${spekVersion}") {
         exclude group: 'org.junit.platform'
         exclude group: 'org.jetbrains.kotlin'
      }
      // spring security
      compile('org.springframework.boot:spring-boot-starter-security')
      testCompile('org.springframework.security:spring-security-test')
      // begin: spring web without jackson
      compile('org.springframework.boot:spring-boot-starter')
      compile('org.springframework.boot:spring-boot-starter-tomcat')
      compile('org.springframework:spring-web')
      compile('org.springframework:spring-webmvc')
      testCompile('org.springframework.boot:spring-boot-starter-test')
      // end: spring web without jackson
      // Keycloak
      compile("org.keycloak:keycloak-spring-security-adapter:${keycloakVersion}")
      compile("org.keycloak:keycloak-spring-boot-adapter:${keycloakVersion}")
      compile("org.keycloak:keycloak-tomcat8-adapter:${keycloakVersion}")
      compile("org.keycloak:keycloak-admin-client:${keycloakVersion}")
      compile("org.jboss.resteasy:resteasy-client:${restEasyClientVersion}")
      compile("org.jboss.resteasy:resteasy-jackson2-provider:${restEasyClientVersion}")

   }

   compileKotlin {
      kotlinOptions.jvmTarget = '1.8'
      kotlinOptions.allWarningsAsErrors = true
   }
   compileTestKotlin {
      kotlinOptions.jvmTarget = '1.8'
   }
}

问题出在 ForkJoinPool.commonPool 的底层 class 加载器,它被 CompletableFuture.supplyAsync 使用。

因为问题和解决方案很复杂为了更好地理解。

此问题仅针对此交叉引用而保留(并可能希望引导其他人找到正确的解决方案)。