(这主要是一个历史问题.Pivotal建议在StackOverflow上进行所有论坛讨论,这就是我在这里提问的原因。)
Spring Boot项目用于证明将应用程序的类和依赖项从可执行jar格式的“顶层”移动到BOOT-INF/
下面的原因是什么?
只是想猜测,似乎这样可以很容易地使用简单的java -xf the-jar.jar BOOT-INF/classes
命令从胖jar中提取与应用程序相关的类和jar。是吗?还有其他原因吗?
答案 0 :(得分:11)
在jar的根目录中打包应用程序类需要Spring Boot的类加载器使用非常规委派模型,并且还会导致Java代理出现问题。
当使用java -jar
启动jar文件时,jar根目录中的所有类都位于系统类加载器的类路径中。在Spring Boot胖jar中,这包括启动器的类,它负责创建一个类加载器,它可以加载应用程序的类及其嵌套在胖jar中的依赖项。
在Spring Boot 1.3及更早版本中,应用程序类打包在胖jar文件的根目录中。这意味着它们位于系统类加载器的类路径中。使用标准的父第一委托模型,这意味着应用程序类将由系统类加载器而不是Spring Boot的类加载器加载。这是有问题的,因为它只是Spring Boot的类加载器,可以从嵌套在fat jar中的依赖项加载类。结果是应用程序无法加载任何依赖项的类。
Spring Boot 1.3通过为其类加载器使用非常规委派模型克服了这个问题。它使用系统类加载器中的URL创建了一个新的类加载器,但没有使用系统类加载器作为父级 - 而是使用了系统类加载器的父级。这意味着Spring Boot的类加载器将用于加载jar的根目录中的应用程序类以及嵌套jar中应用程序依赖项的类。
这种方法有一些缺点。首先是它使Spring Boot的类加载器相当复杂。第二个问题是它打破了Java代理关于如何加载类的假设。我们解决了其中的几个问题,但很明显我们正在打一场失败的战斗。
Spring Boot 1.4重新排列了一个胖jar,将应用程序类放在BOOT-INF/classes
中(它还将嵌套的jar移动到BOOT-INF/lib
,但是从类加载角度来看没有效果)。将应用程序类移动到BOOT-INF/classes
意味着它们不再位于系统类加载器的类路径上。这意味着可以将Spring Boot的类加载器配置为从BOOT-INF/classes
加载来自BOOT-INF/lib
的jar中的类,并使用系统类加载器作为其父级。 Java代理可以打包在jar的根目录中,系统类加载器会像往常一样加载它们。
如需进一步阅读,您可能会对the commit that introduced the change上的消息及其链接的其他问题感兴趣。