为什么Jars中的Jars看不到Jars中其他Jars的内容,如果它们在同一个Jar中?

Sle*_*led 7 jar classloader spring-boot

tl; dr:我们的Spring Boot jar中的类似乎看到了捆绑的jar中的类,但它们的内容似乎无法实现.为什么?


我们的主要产品是一个Web应用程序,但所有业务逻辑都集中在一个核心mac-guffin-api.jar.mac-guffin-api.jar不是一个春天启动的项目,但有一个叫做的Spring Java配置文件net.initech.api.Configuration,初始化所有的服务和仓库等.我们使用MS SQL Server作为我们与后端sqljdbc42:jar驱动程序.

我们需要编写一个需要重用API项目中相同业务逻辑的ETL,因此我们创建了一个Spring Boot Spring Batch项目,该项目mac-guffin-api.jar作为Maven依赖项导入.ETL的配置(net.initech.etl.Configuration)导入的API配置没有问题(我可以从控制台日志中看到它),但是当API配置创建数据库连接时,它无法找到驱动程序.

Caused by: java.lang.ClassNotFoundException: 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:94)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:246)
    ... 113 more
Run Code Online (Sandbox Code Playgroud)

但是,我可以清楚地看到包含驱动程序的JAR存在.ETL的罐子的内容(注: mac-guffin-api.jarsqljdbc42-4.2.jar不是解包,他们是在ETL罐子罐子):

mac-guffin-etl.jar
|
+- org.springframework.boot.loader...
|
+- BOOT-INF
   |
   +- classes
   |  |
   |  +- com.initech.etl.Main.class
   |  |
   |  +- com.initech.etl.Configuration.class
   |
   +- lib
      |
      +- mac-guffin-api.jar
      |  |
      |  +- com.initech.api.Configuration.class
      |
      +- sqljdbc42-4.2.jar
         |
         +- com.microsoft.sqlserver.jdbc.SQLServerDriver.class
Run Code Online (Sandbox Code Playgroud)

所以显然类ETL的配置类可以看到包含的JAR的内容(或者至少是API jar的内容),但是他们的API jar似乎无法com.microsoft.sqlserver.jdbc.SQLServerDriver.class在SQL Server JDBC jar中看到它.

我甚至可以Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver.class" )在Spring上下文实例化之前做一个并且它没有问题.

这是类加载器的限制吗?这是因为API项目不是Spring Boot吗?是因为缺少配置参数?这里发生了什么?

spa*_*kle 2

在配置中的某个位置,您最终得到了用作值的类名:

'com.microsoft.sqlserver.jdbc.SQLServerDriver'
Run Code Online (Sandbox Code Playgroud)

用单引号括起来。通常,正在加载的类名打印时不带引号,双引号或单引号。

这可以解释为什么您能够加载类但 API jar 却不能。检查配置/构建文件中设置驱动程序名称的位置。

演示版

我能收到像你这样的消息的唯一方法:

Caused by: java.lang.ClassNotFoundException: 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
Run Code Online (Sandbox Code Playgroud)

并不是:

Caused by: java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver
Run Code Online (Sandbox Code Playgroud)

就是故意要求加载一个名称中带有单引号的类。例如:

'com.microsoft.sqlserver.jdbc.SQLServerDriver'
Run Code Online (Sandbox Code Playgroud)

输出:

trying my.package.itwontexist
failed to load my.package.itwontexist
java.lang.ClassNotFoundException: my.package.itwontexist
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at myclass.test(myclass.java:10)
    at myclass.main(myclass.java:20)
trying 'my.package.itwontexist'
failed to load 'my.package.itwontexist'
java.lang.ClassNotFoundException: 'my.package.itwontexist'
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at myclass.test(myclass.java:10)
    at myclass.main(myclass.java:21)
Run Code Online (Sandbox Code Playgroud)