如果 Jars in Jars 在同一个 Jar 中,为什么 Jars in Jars 看不到其他 Jars in Jars 的内容?
Why can't Jars in Jars see the contents of other Jars in Jars if they are in the same Jar?
tl;dr: 我们 Spring 引导罐中的 classes 似乎在捆绑罐中看到 classes,但是他们的内容似乎不能。为什么?
我们的主要产品是一个网络应用程序,但所有业务逻辑都集中在一个核心中 mac-guffin-api.jar
。 mac-guffin-api.jar
不是 Spring 引导项目,但有一个 Spring Java 配置文件,名为 net.initech.api.Configuration
,它初始化所有服务和存储库等。我们使用 MS SQL 服务器作为我们的后端和 sqljdbc42:jar
驱动程序。
我们需要编写一个 ETL 来重用 API 项目中的相同业务逻辑,因此我们创建了一个 Spring 引导 Spring 导入 mac-guffin-api.jar
的批处理项目作为 Maven 依赖项。 ETL 的配置 (net.initech.etl.Configuration
) 导入的 APIs 配置没有问题(我可以从控制台日志中看到)但是当 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.jar
and sqljdbc42-4.2.jar
are not unpacked, they are jar in the 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
显然 class ETL 的配置 class 可以看到包含的 JAR 的内容(或者至少是 API jar 的内容),但是它们 API jar 似乎无法在 fellow SQL 服务器 JDBC jar.com.microsoft.sqlserver.jdbc.SQLServerDriver.class
中看到 com.microsoft.sqlserver.jdbc.SQLServerDriver.class
。
我什至可以在实例化 Spring 上下文之前执行 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver.class" )
并且没有问题。
这是 class 加载程序的限制吗?这是因为 API 项目不是 Spring Boot 吗?是因为缺少配置参数吗?这里发生了什么?
在您的配置中的某处,您以 class名称作为值结束:
'com.microsoft.sqlserver.jdbc.SQLServerDriver'
用单引号括起来。通常,正在加载的 class 名称打印时不带引号,双引号或单引号。
这可以解释为什么您可以加载 class 而 API jar 不能。检查 configuration/build 文件以了解设置驱动程序名称的位置。
演示
我能收到像你这样的消息的唯一方法:
Caused by: java.lang.ClassNotFoundException: 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
而不是:
Caused by: java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver
就是故意要求加载一个名字中带单引号的class。例如:
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)
您可能正在从配置中获取驱动值,例如
my.driver = 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
并且该配置返回的值带有单引号。请检查您的配置文件。
您似乎缺少指导 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 所需的加载程序。
示例结构:
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
tl;dr: 我们 Spring 引导罐中的 classes 似乎在捆绑罐中看到 classes,但是他们的内容似乎不能。为什么?
我们的主要产品是一个网络应用程序,但所有业务逻辑都集中在一个核心中 mac-guffin-api.jar
。 mac-guffin-api.jar
不是 Spring 引导项目,但有一个 Spring Java 配置文件,名为 net.initech.api.Configuration
,它初始化所有服务和存储库等。我们使用 MS SQL 服务器作为我们的后端和 sqljdbc42:jar
驱动程序。
我们需要编写一个 ETL 来重用 API 项目中的相同业务逻辑,因此我们创建了一个 Spring 引导 Spring 导入 mac-guffin-api.jar
的批处理项目作为 Maven 依赖项。 ETL 的配置 (net.initech.etl.Configuration
) 导入的 APIs 配置没有问题(我可以从控制台日志中看到)但是当 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.jar
and sqljdbc42-4.2.jar
are not unpacked, they are jar in the 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
显然 class ETL 的配置 class 可以看到包含的 JAR 的内容(或者至少是 API jar 的内容),但是它们 API jar 似乎无法在 fellow SQL 服务器 JDBC jar.com.microsoft.sqlserver.jdbc.SQLServerDriver.class
中看到 com.microsoft.sqlserver.jdbc.SQLServerDriver.class
。
我什至可以在实例化 Spring 上下文之前执行 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver.class" )
并且没有问题。
这是 class 加载程序的限制吗?这是因为 API 项目不是 Spring Boot 吗?是因为缺少配置参数吗?这里发生了什么?
在您的配置中的某处,您以 class名称作为值结束:
'com.microsoft.sqlserver.jdbc.SQLServerDriver'
用单引号括起来。通常,正在加载的 class 名称打印时不带引号,双引号或单引号。
这可以解释为什么您可以加载 class 而 API jar 不能。检查 configuration/build 文件以了解设置驱动程序名称的位置。
演示
我能收到像你这样的消息的唯一方法:
Caused by: java.lang.ClassNotFoundException: 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
而不是:
Caused by: java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver
就是故意要求加载一个名字中带单引号的class。例如:
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)
您可能正在从配置中获取驱动值,例如
my.driver = 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
并且该配置返回的值带有单引号。请检查您的配置文件。
您似乎缺少指导 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 所需的加载程序。
示例结构:
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