Spring 使用外部 JAR 启动时出现 NoClassDefFoundError

Spring Boot with External JAR gets NoClassDefFoundError

所以我的主项目中有一组子项目。第一个项目(在下面的示例中称为 mainCode)实现了应用程序的全部功能。但是,WidgetAImplWidgetBImpl 被分成单独的子项目,以便在需要修复时可以独立部署。 WidgetAImplWidgetBImpl 类 与接口 WidgetInterface.

位于同一个包中

项目布局如下:

project
    + mainCode
        + /src/main/java/com/mycompany/myproject/Application.java
        + /src/main/java/com/mycompany/myproject/AppConfig.java
        + /src/main/java/com/mycompany/myproject/widgets/WidgetInterface.java
    + widgets
        + WidgetA
            + /src/main/java/com/mycompany/myproject/widgets/WidgetAImpl.java
        + WidgetB
            + /src/main/java/com/mycompany/myproject/widgets/WidgetBImpl.java

我 gradle 以这样的方式构建这些 mainCode 项目成为我们的 war 文件以部署到 tomcat 并且每个小部件都变成一个 jar文件。目标是将 jar 文件添加到 tomcat 的类路径中,并单独部署 war 文件。我花了一天时间 Gradle 来构建 WidgetAImpl 项目,方法是将 mainCode 项目声明为依赖项。所以 Gradle 现在可以正确构建它们,我有一个 mainCode.war 和一个 WidgetAImpl.jar 和一个 WidgetBImpl.jar 文件。在实现中,小部件都与一个自动装配的 HashMap 聚集在一起,因此所有已实现的小部件都在 运行 时间在单个 HashMap 中创建。我已经通过 catalina.properties sharedLoader 属性.

将小部件添加到 tomcat 的类路径中

我的问题是,当我将 war 文件部署到 tomcat 时,出现以下错误(针对 space 进行了修剪):

2017-11-15 21:55:39.841  WARN 16687 --- [nio-8443-exec-4] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.scheduling.annotation.ProxyAsyncConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [com.mycompany.myproject.widgets.WidgetAImpl] for bean with name 'WidgetAImpl' defined in URL [jar:file:/mycompany/libs/WidgetAImpl.jar!/com/mycompany/myproject/widgets/WidgetAImpl.class]: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: com/mycompany/myproject/widgets/WidgetInterface
2017-11-15 21:55:39.845 ERROR 16687 --- [nio-8443-exec-4] o.s.b.f.s.DefaultListableBeanFactory     : Destroy method on bean with name 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor' threw an exception

java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@3633386f: startup date [Wed Nov 15 21:55:38 UTC 2017]; root of context hierarchy
    at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:404) [spring-context-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.context.support.ApplicationListenerDetector.postProcessBeforeDestruction(ApplicationListenerDetector.java:97) ~[spring-context-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:253) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:578) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:554) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:954) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:523) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:961) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1033) [spring-context-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:555) [spring-context-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) [spring-boot-1.4.2.RELEASE.jar:1.4.2.RELEASE]


...


Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [com.mycompany.myproject.widgets.WidgetAImpl] for bean with name 'WidgetAImpl' defined in URL [jar:file:/mycompany/libs/WidgetAImpl.jar!/com/mycompany/myproject/widgets/WidgetAImpl.class]: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: com/mycompany/myproject/widgets/WidgetInterface
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1364) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:639) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:607) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1456) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:420) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:390) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:220) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1243) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1164) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1089) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1059) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:663) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    ... 68 common frames omitted
Caused by: java.lang.NoClassDefFoundError: com/mycompany/myproject/widgets/WidgetInterface
    at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_144]
    at java.lang.ClassLoader.defineClass(Unknown Source) ~[na:1.8.0_144]
    at java.security.SecureClassLoader.defineClass(Unknown Source) ~[na:1.8.0_144]
    at java.net.URLClassLoader.defineClass(Unknown Source) ~[na:1.8.0_144]
    at java.net.URLClassLoader.access0(Unknown Source) ~[na:1.8.0_144]
    at java.net.URLClassLoader.run(Unknown Source) ~[na:1.8.0_144]
    at java.net.URLClassLoader.run(Unknown Source) ~[na:1.8.0_144]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_144]
    at java.net.URLClassLoader.findClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.Class.forName0(Native Method) ~[na:1.8.0_144]
    at java.lang.Class.forName(Unknown Source) ~[na:1.8.0_144]
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1271) ~[catalina.jar:8.5.20]
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119) ~[catalina.jar:8.5.20]
    at org.springframework.util.ClassUtils.forName(ClassUtils.java:250) ~[spring-core-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:394) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1408) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1353) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    ... 79 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.mycompany.myproject.widgets.WidgetInterface
    at java.net.URLClassLoader.findClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_144]
    ... 98 common frames omitted

(完整异常堆栈跟踪:https://pastebin.com/KwaNGaeN

由此看来Spring找不到我的WidgetInterface。但是,我已经确认 war 文件中的 WidgetInterface 类文件 。此外,它与 WidgetAImpl 和 WidgetBImpl 是相同的 java 包(即使它们位于不同的文件中)。为什么它看不到 WidgetInterface?

你能用 WidgetInterface.java 创建 commonbase 模块吗?将其作为小部件添加到 Tomcat。然后所有其他罐子, war 可以依赖那个公共部分。 Tomcat 将有清晰的路径如何加载它,其他模块使用什么。