使用 Spring 中的类路径启动子进程导致 ClassNotFoundException
Start subprocess with classpath in Spring results in ClassNotFoundException
我有一个应用程序通过 Java 的内部 java.lang.ProcessHandler
在子进程中启动另一个 class。很简单:
public MySubProcessHandler() {
// setup commands
String javaHome = System.getProperty(ProcessKeys.JAVA_HOME);
String javaBin = Paths.get(javaHome, "bin", "java").toString();
String classpath = System.getProperty(ProcessKeys.JAVA_CLASSPATH);
String className = MyServer.class.getName();
List<String> command = new LinkedList<>();
command.add(javaBin);
command.add("-classpath");
command.add(classpath);
command.add(className);
// setup sub process
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.start();
}
现在在这个应用程序和 IntelliJ 中一切正常。
但是,我也在 Spring-Boot 应用程序中使用它。当我启动可执行 JAR 并想要创建子进程时,该子进程的错误流向我发送:
Error: Could not find or load main class my.path.to.MyServer
Caused by: java.lang.ClassNotFoundException: my.path.to.MyServer
当我从 Spring 应用程序检查可执行 jar 时,它确实只包含内部项目 classes 但没有依赖项。依赖项在 BOOT-INF/lib/my-server.jar
问题:我该怎么做 Spring 加载这个 class 以便 MySubProcessHandler
能够找到它?
我当前的方法: 首先,我想我可以通过将 MyServer
设置为 @Bean
来解决这个问题,以强制 Spring 加载 class。不幸的是,它似乎没有用。我发现 IntelliJ 在启动应用程序时会自动将依赖的 Maven 存储库添加到 class 路径,这解决了问题!所以使用 IntelliJ 它工作得很好。但是,考虑到所需的 jar(带有“缺失”class)已经在 fat jar 中,在启动时手动将 maven 依赖项添加到 class 路径看起来是错误的。必须有另一种解决方案。
正如已经指出的那样,Spring 使用不同的方式启动(在 spring 中,术语正在启动)或加载 classes。 Spring's doc 关于“The Executable Jar Format”中对此有很好的解释。
通常,可以直接通过以下命令启动主class:
java -cp my-project.jar my.path.to.MyServer
但如果 my-project.jar
是来自 Spring 的可执行 JAR,则需要使用 Spring 的 PropertiesLauncher
进行加载和 启动。您可以通过以下方式执行此操作:
java -cp my-spring-project.jar \
-Dload.main=my.path.to.MyServer \
org.springframework.boot.loader.PropertiesLauncher
所以这只是通过 spring 引导启动 JVM 并通过 Spring 的 PropertiesLauncher
加载给定的主程序 class。根据此命令从上方更新 MySubProcessHandler()
的代码可解决此问题。
我有一个应用程序通过 Java 的内部 java.lang.ProcessHandler
在子进程中启动另一个 class。很简单:
public MySubProcessHandler() {
// setup commands
String javaHome = System.getProperty(ProcessKeys.JAVA_HOME);
String javaBin = Paths.get(javaHome, "bin", "java").toString();
String classpath = System.getProperty(ProcessKeys.JAVA_CLASSPATH);
String className = MyServer.class.getName();
List<String> command = new LinkedList<>();
command.add(javaBin);
command.add("-classpath");
command.add(classpath);
command.add(className);
// setup sub process
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.start();
}
现在在这个应用程序和 IntelliJ 中一切正常。 但是,我也在 Spring-Boot 应用程序中使用它。当我启动可执行 JAR 并想要创建子进程时,该子进程的错误流向我发送:
Error: Could not find or load main class my.path.to.MyServer
Caused by: java.lang.ClassNotFoundException: my.path.to.MyServer
当我从 Spring 应用程序检查可执行 jar 时,它确实只包含内部项目 classes 但没有依赖项。依赖项在 BOOT-INF/lib/my-server.jar
问题:我该怎么做 Spring 加载这个 class 以便 MySubProcessHandler
能够找到它?
我当前的方法: 首先,我想我可以通过将 MyServer
设置为 @Bean
来解决这个问题,以强制 Spring 加载 class。不幸的是,它似乎没有用。我发现 IntelliJ 在启动应用程序时会自动将依赖的 Maven 存储库添加到 class 路径,这解决了问题!所以使用 IntelliJ 它工作得很好。但是,考虑到所需的 jar(带有“缺失”class)已经在 fat jar 中,在启动时手动将 maven 依赖项添加到 class 路径看起来是错误的。必须有另一种解决方案。
正如已经指出的那样,Spring 使用不同的方式启动(在 spring 中,术语正在启动)或加载 classes。 Spring's doc 关于“The Executable Jar Format”中对此有很好的解释。
通常,可以直接通过以下命令启动主class:
java -cp my-project.jar my.path.to.MyServer
但如果 my-project.jar
是来自 Spring 的可执行 JAR,则需要使用 Spring 的 PropertiesLauncher
进行加载和 启动。您可以通过以下方式执行此操作:
java -cp my-spring-project.jar \
-Dload.main=my.path.to.MyServer \
org.springframework.boot.loader.PropertiesLauncher
所以这只是通过 spring 引导启动 JVM 并通过 Spring 的 PropertiesLauncher
加载给定的主程序 class。根据此命令从上方更新 MySubProcessHandler()
的代码可解决此问题。