RawImageInputStream 的 JAI ImageIO NoClassDefFoundError

JAI ImageIO NoClassDefFoundError for RawImageInputStream

我对这个问题很困惑。我在 jai-imageio-core 上使用 1.3.0 版本,请检查下面的依赖关系,我完全不知道如何从 jar 中找出一个 class 文件(RawImageReaderSpi)如何加载到 JVM 但 RawImageInputStream 不是已加载。

我已经在 tomcat 7 容器中部署了我的 spring 引导 Web 服务。

这个问题是随机弹出的,我注意到当我重新启动 TC 容器并部署新版本的服务时,这个问题会间歇性地出现。

非常感谢任何线索。

我花了一天时间想弄清楚哪里出了问题,最后觉得自己很蠢

<dependency>
    <groupId>com.github.jai-imageio</groupId>
    <artifactId>jai-imageio-core</artifactId>
    <version>1.3.0</version>
</dependency>

<dependency>
    <groupId>com.github.jai-imageio</groupId>
    <artifactId>jai-imageio-jpeg2000</artifactId>
    <version>1.3.0</version>
</dependency>
java.lang.NoClassDefFoundError: com/github/jaiimageio/stream/RawImageInputStream
        at com.github.jaiimageio.impl.plugins.raw.RawImageReaderSpi.canDecodeInput(RawImageReaderSpi.java:102) ~[jai-imageio-core-1.3.0.jar:1.3.0]
        at javax.imageio.ImageIO$CanDecodeInputFilter.filter(ImageIO.java:567) ~[na:1.8.0_121]
        at javax.imageio.spi.FilterIterator.advance(ServiceRegistry.java:821) ~[na:1.8.0_121]
        at javax.imageio.spi.FilterIterator.(ServiceRegistry.java:815) ~[na:1.8.0_121]
        at javax.imageio.spi.ServiceRegistry.getServiceProviders(ServiceRegistry.java:516) ~[na:1.8.0_121]
        at javax.imageio.ImageIO.getImageReaders(ImageIO.java:646) ~[na:1.8.0_121]
        at javax.imageio.ImageIO.read(ImageIO.java:1438) ~[na:1.8.0_121]
        at javax.imageio.ImageIO.read(ImageIO.java:1352) ~[na:1.8.0_121]
        at my-package.a.b(a.java:155) ~[classes/:na]
        at my-package.a.b(a.java:181) ~[classes/:na]
        at my-package.a.b(a.java:84) ~[classes/:na]
        at my-package.a$$FastClassBySpringCGLIB$f66283f.invoke() ~[spring-core-4.1.3.RELEASE.jar:na]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717) ~[spring-aop-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:267) ~[spring-tx-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653) ~[spring-aop-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at my-package.a$$EnhancerBySpringCGLIB$$dfc04a57.b() ~[spring-core-4.1.3.RELEASE.jar:na]
        at my-package.a.b(c.java:165) ~[classes/:na]
        at my-package.a$$FastClassBySpringCGLIB$dbe4b2.invoke() ~[spring-core-4.1.3.RELEASE.jar:na]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717) ~[spring-aop-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:267) ~[spring-tx-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653) ~[spring-aop-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at my-package.a$$EnhancerBySpringCGLIB$$d153236a.processApprovedApplications() ~[spring-core-4.1.3.RELEASE.jar:na]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
        at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) ~[spring-context-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) [spring-context-4.1.3.RELEASE.jar:4.1.3.RELEASE]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_121]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_121]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_121]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_121]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_121]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_121]

这里的问题是 ImageIO 插件和容器(如 web/servlet 容器)的已知问题。 ImageIO 不支持将插件部署为 Web 应用程序的一部分。

跟踪已注册插件的 ImageIO registry 实际上是 JVM 全局的(它实际上是每个应用程序上下文的注册表,但是,通常只有一个应用程序上下文* ).

  • 最安全的选择是在容器的 "shared" 或 "common" lib 文件夹中安装插件(及其所有依赖项)。这确保插件仅安装一次,并且它们可用于所有容器的上下文。这需要控制容器环境,并且由于需要手动安装和完整的容器重启,升级起来也很麻烦。

  • 如果您更喜欢将插件部署为 Web 应用程序的一部分,我所知道的唯一选择是使用 com.twelvemonkeys.servlet.image.IIOProviderContextListener,如 here 所述。

    或者,要在没有 web.xml 文件的 Spring 启动项目中使用它,您可以将以下行添加到 SpringBootServletInitializer 子程序中的 onStartup() 方法class 描述 here:

    servletContext.addListener(IIOProviderContextListener.class);
    

    请注意,您必须在首次启用上下文侦听器后完全重启容器,因为ImageIO可能已经"polluted" 来自早期的部署。

*) 不要与容器的 Web 应用程序上下文混淆,后者可能有很多。


[How can it be that] one class file (RawImageReaderSpi) is loaded in JVM but RawImageInputStream is not loaded?

这就是 link 中提到的情况,其中一个较早部署的 Web 应用程序已在注册表中注册了 RawImageReaderSpi class,而其余的 class由于重新部署,该部署中的 es 已被删除。 RawImageInputStream class 可能 从新部署中可用,但由于新的 Web 应用程序上下文使用不同的 ClassLoader 实例,原始 Spi class 看不到它正在寻找的 class。