【问题标题】:Why can't Jars in Jars see the contents of other Jars in Jars if they are in the same Jar?为什么 Jars in Jars 看不到其他 Jars in Jars 在同一个 Jar 中的内容?
【发布时间】:2017-09-13 12:21:02
【问题描述】:

tl;dr: 我们的 Spring Boot jar 中的类似乎可以看到捆绑 jar 中的类,但它们的内容似乎不能。为什么?


我们的主要产品是一个网络应用程序,但所有业务逻辑都集中在一个核心 mac-guffin-api.jar 中。 mac-guffin-api.jar 不是 Spring Boot 项目,但有一个名为 net.initech.api.Configuration 的 Spring Java 配置文件,用于初始化所有服务和存储库等。我们使用 MS SQL Server 作为我们的后端,sqljdbc42:jar司机。

我们需要编写一个 ETL,该 ETL 需要重用 API 项目中的相同业务逻辑,因此我们创建了一个 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

但是,我可以清楚地看到包含驱动程序的 JAR 存在。 ETL jar 的内容是(Nb: mac-guffin-api.jarsqljdbc42-4.2.jar 未解包,它们是 ETL jar 中的 jar):

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

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

我什至可以在 Spring 上下文实例化之前执行 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver.class" ) 并且没有问题。

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

【问题讨论】:

  • 有趣的问题...可能是一些事情。你有什么方法可以在 GitHub 上分享一个非常小的、可重复的项目吗? stackoverflow.com/help/mcve
  • 我记不太清了,但是类名总是出现在引号中吗?单引号也似乎有点非java

标签: spring-boot jar classloader


【解决方案1】:

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

'com.microsoft.sqlserver.jdbc.SQLServerDriver'

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

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

演示

只有这样我才能收到像你这样的消息:

Caused by: java.lang.ClassNotFoundException: 'com.microsoft.sqlserver.jdbc.SQLServerDriver'

而不是:

Caused by: java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver

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

import java.lang.*;

public class myclass {

        public static void test(String thename) {
                System.out.println("trying " + thename);
                try {
                        myclass test = (myclass) myclass.class
                                .getClassLoader()
                                .loadClass(thename)
                                .newInstance();
                        System.out.println(test.toString());
                } catch (Exception e){
                        System.out.println("failed to load " + thename);
                        e.printStackTrace();
                }
        }

        public static void main(String[] args) {
                test("my.package.itwontexist");
                test("'my.package.itwontexist'");
        }
}

输出:

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)

【讨论】:

    【解决方案2】:

    您可能从配置中获取驱动程序价值,例如

    my.driver = 'com.microsoft.sqlserver.jdbc.SQLServerDriver'

    并且该配置使用单引号返回值。请检查您的配置文件。

    【讨论】:

      【解决方案3】:

      您似乎缺少指导 Spring 如何加载嵌套 jar 的 MANFIEST.MF 文件。这是 Spring 文档中的示例层次结构。你可以阅读如何配置它by going here

      MANIFEST.MF 应该包含这个(对于下面的结构):

      Main-Class: org.springframework.boot.loader.JarLauncher
      Start-Class: com.mycompany.project.MyApplication
      

      Start-Class 是您进入应用程序的入口点 Main-Class 是加载嵌套 jar 所需的 Loader。

      示例结构:

      example.war
       |
       +-META-INF
       |  +-MANIFEST.MF
       +-org
       |  +-springframework
       |     +-boot
       |        +-loader
       |           +-<spring boot loader classes>
       +-WEB-INF
          +-classes
          |  +-com
          |     +-mycompany
          |        +-project
          |           +-YourClasses.class
          +-lib
          |  +-dependency1.jar
          |  +-dependency2.jar
          +-lib-provided
             +-servlet-api.jar
             +-dependency3.jar
      

      【讨论】:

        猜你喜欢
        • 2013-08-14
        • 2018-09-22
        • 1970-01-01
        • 1970-01-01
        • 2013-10-01
        • 2020-03-10
        • 1970-01-01
        • 1970-01-01
        • 2012-07-05
        相关资源
        最近更新 更多