switch使用未定义的内部类在tomcat中导致NoClassDefFoundError

F.P*_*F.P 7 java tomcat

我有一个非常简单的枚举

my.package.data.util

public enum Mode
{
    SQLEXPORT, PREVIEW, PRINT
}
Run Code Online (Sandbox Code Playgroud)

应该使用哪个在另一个类中使用枚举

my.package.program.ComponentController

switch (_mode) { // line 278, _mode is of type my.package.data.util.Mode
case PREVIEW:
    // Do thing for preview
    break;
case SQLEXPORT:
    // Do thing for SQL
    break;
case PRINT:
    // Do thing for print
    break;
default:
    throw new IllegalArgumentException();
}
Run Code Online (Sandbox Code Playgroud)

这两个类在同一个项目中,并编译成一个jar文件.

然后,Web项目正在使用此库(放入WEB-INF/lib文件夹中).但是,当需要使用这个库,特别是那个开关时,NoClassDefFoundError会发生:

NoClassDefFoundError:my/package/program/ComponentController $ 1

at my.package.program.ComponentController.doCall(ComponentController.java:278)

这是我在几个层面上无法理解的:

  1. 为什么Java尝试加载内部类(如可见$1).没有内在的课程ComponentController,也从来没有.
  2. 为什么Java认为交换机使用这个内部类作为其参数
  3. my.package.data.util.Mode类消失

这里发生了什么?


更多信息不在原始问题中

  • ComponentController扩展另一个类,SessionBuilder.这个类也没有内部类

我反编译了ComponentController使用javap并试图在结果字节代码中找到有趣的东西.

看来字节码中确实有一个内部类:

public class my.package.program.ComponentController extends my.other.package.SessionBuilder
  SourceFile: "ComponentController.java"
  InnerClasses:
       static #192 of #2; //class my/package/program/ComponentController$1 of class my/package/program/ComponentController
Run Code Online (Sandbox Code Playgroud)

每当my.package.data.util.Mode引用时都使用此类:

#192 = Class              #486          //  my/package/program/ComponentController$1
#193 = Utf8               
#194 = Utf8               InnerClasses
#195 = Utf8               _mode
#196 = Utf8               Lmy/package/data/util/Mode;
Run Code Online (Sandbox Code Playgroud)

而且,当切换实际发生时:

183: getstatic     #102                // Field my/package/program/ComponentController$1.$SwitchMap$my$package$data$util$Mode:[I
186: aload_0       
187: getfield      #5                  // Field _mode:Lmy/package/data/util/Mode;
190: invokevirtual #103                // Method my/package/data/util/Mode.ordinal:()I
193: iaload        
194: tableswitch   { // 1 to 3
   1: 220
   2: 335
   3: 440
   default: 516
}
Run Code Online (Sandbox Code Playgroud)

Rich链接的问题的进一步调查产生了一些有趣的东西:jar从库项目构建的本地tomcat安装和用于生成jar服务器的那个不同:

左:WEB-INF/lib通过eclipse通过本地tomcat的jar ,右:jar由ANT构建

JAR Diff

现在看来,当发布到本地tomcat时eclipse使用的构建过程与ANT的不同(这是AFAIK只是一个简单的javac调用)


好吧,现在我只是将jarANT创建的文件复制到本地的tomcats中WEB-INF/lib,一切正常.当然,这意味着在库项目中的每次更改之后,我都必须手动将新文件复制jar到我的tomcat库中.

我在eclipse上将其作为错误报告提交,并将报告任何新闻.

Ric*_*ich 8

根据这个问题:

...每次在Enum上使用开关时,Sun的Javac 1.6都会创建一个额外的合成类.

根据这个问题,你并不孤单.