【发布时间】:2018-03-05 14:55:36
【问题描述】:
我解决了一个非常具体的问题,其解决方案似乎是基本的:
我的 (Spring) 应用程序的类加载器层次结构是这样的:SystemClassLoader -> PlatformClassLoader -> AppClassLoader
如果我使用 Java CompleteableFuture 来运行线程。线程的ContextClassLoader 是:SystemClassLoader -> PlatformClassLoader -> ThreadClassLoader
因此,我无法访问 AppClassLoader 中的任何类,尽管我必须这样做,因为所有外部库类都驻留在那里。
源代码库非常大,所以我不想/不能将所有与线程相关的部分重写为其他内容(例如,将自定义执行程序传递给每个调用)。
所以我的问题是:我怎样才能使创建的线程例如CompleteableFuture.supplyAsync() 使用 AppClassLoader 作为父级?(而不是 PlatformClassloader)
我发现ForkJoinPool 用于创建线程。但在我看来,一切都是static和final。所以我怀疑即使设置一个带有系统属性的自定义ForkJoinWorkerThreadFactory 在这种情况下也会有所帮助。还是会?
编辑回答 cmets 的问题:
-
你在哪里部署?这是否在 jetty / tomcat / 任何 JEE 容器中运行?
- 我正在使用默认的 Spring Boot 设置,因此使用了内部的 tomcat 容器。
-
您遇到的具体问题是什么?
- 确切的问题是:java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource 从类加载器中不可见
-
您提交给 supplyAsync() 的作业是从 AppClassLoader 创建的,不是吗?
supplyAsync是从使用AppClassLoader的MainThread调用的。但是,调试应用程序显示所有此类线程都将PlatformClassLoader作为其父级。据我了解,这是因为ForkJoinPool.commonPool() 是在应用程序启动期间构造的(因为它是静态的),因此使用默认的类加载器作为PlatformClassLoader的父级。因此,该池中的所有线程都将PlatformClassLoader作为ContextClassLoader 的父级(而不是AppClassLoader)。
1234563 .这似乎证实了我在第一种情况下的假设,即公共池不是由
MainThread创建的,至少在它使用AppClassLoader本身时不是。
完整的堆栈跟踪:
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$1(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]
【问题讨论】:
-
你部署到哪里?这是否在 jetty / tomcat / 任何 JEE 容器中运行?
-
您遇到的具体问题是什么?您提交给
supplyAsync()的作业是从AppClassLoader创建的,不是吗?所以他们应该可以访问它的类。这对我来说就像一个XY problem。 -
感谢你们两位的cmets!请在上面编辑过的问题中找到答案。谢谢!
-
您应该提供异常的完整堆栈跟踪。此外,这可能是
keycloak特有的——因此相应的标签可能是相关的。 -
所以这与您在Spring Boot / Security classloader issues with Keycloak run from terminal 中遇到的问题完全相同。你最后解决了吗?我还注意到此错误消息特定于 Java 9。您是否尝试过 8?这可能是一种回归。
标签: java spring classloader completable-future