如何在不重新启动 JVM 的情况下将外部 JAR 添加到 Spring 应用程序?
How to add external JARs to Spring application without restarting JVM?
我有一个 Spring 引导应用程序,它根据特定条件将外部 JAR 文件复制到文件夹中。这些 JAR 可以包含许多 Spring 组件(即 类 用 @Component
注释或元注释)并且 Spring 应用程序应该能够扫描和实例化这些 bean。是否可以根据特定条件动态 加载 JAR 文件的内容并使它们可用于 Spring 应用程序上下文? 我完全了解这带来的安全隐患。
我已经阅读了 Spring 为其 executable JAR format, such as JarLauncher
and PropertiesLauncher
提供的不同类型的 Launcher
,但看起来这些启动器没有检测到类路径的更改,但是而是只扫描一次 JAR 文件的目录。
下面的简单应用程序演示了这个问题:
// .../Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
System.out.println("Please copy JAR files and press Enter ...");
System.in.read();
SpringApplication.run(Application.class, args);
}
}
将默认的 JarLauncher
替换为 PropertiesLauncher
:
// build.gradle
tasks.named('bootJar') {
manifest {
attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher',
'Start-Class': 'com.example.customlauncher.Application'
}
}
在 PropertiesLauncher
:
的属性文件中指定外部 JAR 的位置
# .../resources/loader.properties
loader.path=file:/path/to/dir
应用程序是 Spring Initializer Gradle application 并由 运行 bootJar
任务打包:./gradlew bootJar
.
然后使用以下命令启动它:
java -jar build/libs/customlauncher-0.0.1-SNAPSHOT.jar
如果 JAR 文件已存在于指定位置 (/path/to/dir
),则此方法有效,但如果在目录为空且 JAR 文件为空时执行 java
命令,则此方法无效然后在应用程序等待用户复制文件并按 Enter ↲.
时复制
有几个相关的问题,但看起来他们都假定 JAR 文件在启动 JVM 时已经存在:
- How to put a directory first on the classpath with Spring Boot?
有没有一种方法可以在不使用太多笨拙的 hack 的情况下实现这一目标?或者建议使用 OSGi 之类的东西?我是不是完全看错了,有更好的方法让类路径上的 JAR 不需要总是需要加载(如果 JAR 被“禁用”,它不应该被 JVM loaded/compiled,不应该被 Spring 等捡起)?
如果在启动 Spring 应用程序之前复制 JAR 文件,这似乎是可能的。感觉很骇人听闻,但确实有效。 使用风险自负!
您需要两个 class,一个用于引导外部 JAR,然后通过手动创建的 PropertiesLauncher
启动第二个。引导 class 可以是普通的旧常规 Java class(但它也可以是 Spring 引导应用程序)并且只需要第二个 class一个 SpringBootApplication.
// BootstrapApplication.java
public class BootstrapApplication {
public static void main(String[] args) {
System.out.println("Please copy JAR files and press Enter ...");
System.in.read();
PropertiesLauncher.main(args);
}
}
// Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在 gradle 文件中,我们可以切换回默认 JarLauncher
,方法是删除 bootJar
任务清单配置并通过 springBoot
配置块应用设置. mainClass
将在 MANIFEST.MF
文件中以 Start-Class
结束。
// build.gradle
springBoot {
mainClass = 'com.example.customlauncher.BootstrapApplication'
}
在加载程序的属性文件中,需要设置一个新的属性,它指向真实的应用程序class。此文件中的设置仅由 PropertiesLauncher
选取并被 JarLauncher
忽略。换句话说:JarLauncher
从清单文件中委托给 Start-Class
,PropertiesLauncher
从其属性文件中委托给 loader.main
。
# .../resources/loader.properties
loader.path=file:/path/to/dir
loader.main=com.example.customlauncher.Application
Spring(启动)将首先调用 BootstrapApplication
的主要方法,如 MANIFEST.MF
文件中指定(通过 springBoot
配置块控制 build.gradle
文件)。在此 main 方法的实现中,创建了一个新的 PropertiesLauncher
,并将 main class 设置为“真实”应用程序(即 Application
)。
应用程序的执行仍然是通过相同的调用完成的:
java -jar build/libs/customlauncher-0.0.1-SNAPSHOT.jar
在 JVM 启动后添加到 /path/to/dir
,但在 之前 在 BootstrapApplication 中调用 PropertiesLauncher#main
的任何 JAR 文件然后在 class从 Application
.
中看到的路径和应用程序上下文
我有一个 Spring 引导应用程序,它根据特定条件将外部 JAR 文件复制到文件夹中。这些 JAR 可以包含许多 Spring 组件(即 类 用 @Component
注释或元注释)并且 Spring 应用程序应该能够扫描和实例化这些 bean。是否可以根据特定条件动态 加载 JAR 文件的内容并使它们可用于 Spring 应用程序上下文? 我完全了解这带来的安全隐患。
我已经阅读了 Spring 为其 executable JAR format, such as JarLauncher
and PropertiesLauncher
提供的不同类型的 Launcher
,但看起来这些启动器没有检测到类路径的更改,但是而是只扫描一次 JAR 文件的目录。
下面的简单应用程序演示了这个问题:
// .../Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
System.out.println("Please copy JAR files and press Enter ...");
System.in.read();
SpringApplication.run(Application.class, args);
}
}
将默认的 JarLauncher
替换为 PropertiesLauncher
:
// build.gradle
tasks.named('bootJar') {
manifest {
attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher',
'Start-Class': 'com.example.customlauncher.Application'
}
}
在 PropertiesLauncher
:
# .../resources/loader.properties
loader.path=file:/path/to/dir
应用程序是 Spring Initializer Gradle application 并由 运行 bootJar
任务打包:./gradlew bootJar
.
然后使用以下命令启动它:
java -jar build/libs/customlauncher-0.0.1-SNAPSHOT.jar
如果 JAR 文件已存在于指定位置 (/path/to/dir
),则此方法有效,但如果在目录为空且 JAR 文件为空时执行 java
命令,则此方法无效然后在应用程序等待用户复制文件并按 Enter ↲.
有几个相关的问题,但看起来他们都假定 JAR 文件在启动 JVM 时已经存在:
- How to put a directory first on the classpath with Spring Boot?
有没有一种方法可以在不使用太多笨拙的 hack 的情况下实现这一目标?或者建议使用 OSGi 之类的东西?我是不是完全看错了,有更好的方法让类路径上的 JAR 不需要总是需要加载(如果 JAR 被“禁用”,它不应该被 JVM loaded/compiled,不应该被 Spring 等捡起)?
如果在启动 Spring 应用程序之前复制 JAR 文件,这似乎是可能的。感觉很骇人听闻,但确实有效。 使用风险自负!
您需要两个 class,一个用于引导外部 JAR,然后通过手动创建的 PropertiesLauncher
启动第二个。引导 class 可以是普通的旧常规 Java class(但它也可以是 Spring 引导应用程序)并且只需要第二个 class一个 SpringBootApplication.
// BootstrapApplication.java
public class BootstrapApplication {
public static void main(String[] args) {
System.out.println("Please copy JAR files and press Enter ...");
System.in.read();
PropertiesLauncher.main(args);
}
}
// Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在 gradle 文件中,我们可以切换回默认 JarLauncher
,方法是删除 bootJar
任务清单配置并通过 springBoot
配置块应用设置. mainClass
将在 MANIFEST.MF
文件中以 Start-Class
结束。
// build.gradle
springBoot {
mainClass = 'com.example.customlauncher.BootstrapApplication'
}
在加载程序的属性文件中,需要设置一个新的属性,它指向真实的应用程序class。此文件中的设置仅由 PropertiesLauncher
选取并被 JarLauncher
忽略。换句话说:JarLauncher
从清单文件中委托给 Start-Class
,PropertiesLauncher
从其属性文件中委托给 loader.main
。
# .../resources/loader.properties
loader.path=file:/path/to/dir
loader.main=com.example.customlauncher.Application
Spring(启动)将首先调用 BootstrapApplication
的主要方法,如 MANIFEST.MF
文件中指定(通过 springBoot
配置块控制 build.gradle
文件)。在此 main 方法的实现中,创建了一个新的 PropertiesLauncher
,并将 main class 设置为“真实”应用程序(即 Application
)。
应用程序的执行仍然是通过相同的调用完成的:
java -jar build/libs/customlauncher-0.0.1-SNAPSHOT.jar
在 JVM 启动后添加到 /path/to/dir
,但在 之前 在 BootstrapApplication 中调用 PropertiesLauncher#main
的任何 JAR 文件然后在 class从 Application
.