如何在不重新启动 JVM 的情况下将外部 JAR 添加到 Spring 应用程序? [英] How to add external JARs to Spring application without restarting JVM?

查看:72
本文介绍了如何在不重新启动 JVM 的情况下将外部 JAR 添加到 Spring 应用程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 Spring Boot 应用程序,它根据特定条件将外部 JAR 文件复制到一个文件夹中.这些 JAR 可以包含许多 Spring 组件(即用 @Component 注释或元注释的类),并且 Spring 应用程序应该能够扫描和实例化这些 bean.是否可以根据某些条件动态加载 JAR 文件的内容并使它们可用于 Spring 应用程序上下文?我完全意识到这会带来安全隐患.

I have a Spring Boot application which copies external JAR files to a folder, depending on certain conditions. These JARs can contain many Spring components (i.e. classes annotated or meta-annotated with @Component) and the Spring application should be able scan and instantiate for these beans. Is it possible, based on certain conditions, to dynamically load the contents of the JAR files and make them available to the Spring application context? I am fully aware of the security implications this has.

我已经阅读了 Spring 为其 可执行JAR格式,如JarLauncherPropertiesLauncher,但它看起来就像这些启动器不会检测对类路径的更改,而是只扫描一次目录以查找 JAR 文件.

I have read about the different types of Launchers which Spring provides for its executable JAR format, such as JarLauncher and PropertiesLauncher, but it looks like that these launchers do not detect changes to the classpath, but instead only scan the directories once for JAR files.

以下简单的应用程序演示了这个问题:

The following simple application demonstrates the problem:

// .../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);
  }
}

PropertiesLauncher 替换默认的 JarLauncher:

// build.gradle
tasks.named('bootJar') {
  manifest {
    attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher',
      'Start-Class': 'com.example.customlauncher.Application'
  }
}

PropertiesLauncher 的属性文件中指定外部 JAR 的位置:

Specify the location to the external JARs in the properties file of the PropertiesLauncher:

# .../resources/loader.properties
loader.path=file:/path/to/dir

应用程序是一个 Spring Initializer Gradle应用程序和通过运行 bootJar 任务打包:./gradlew bootJar.

The application is a Spring Initializer Gradle application and packaged by running the bootJar task: ./gradlew bootJar.

然后使用以下命令启动:

It is then started with the following command:

java -jar build/libs/customlauncher-0.0.1-SNAPSHOT.jar

如果 JAR 文件已经存在于指定位置 (/path/to/dir),则此方法有效,但如果执行 java 命令,则此方法无效当目录为空时,然后在应用程序等待用户复制文件并按 Enter 时复制 JAR 文件.

This works if the JAR file is already present at the specified location (/path/to/dir), but it does not work if the java command is executed while the directory is empty and the JAR file is then copied while the app waits for the user to copy the files and press Enter .

有几个相关的问题,但看起来他们都假设启动 JVM 时 JAR 文件已经存在:

There are a couple of related questions, but it looks like they all assume that the JAR files already exist at the time of starting the JVM:

有没有办法在没有太多尴尬的黑客的情况下实现这一目标?或者建议使用类似 OSGi?我是否认为这是完全错误的,并且有更好的方法在类路径上放置不需要总是需要加载的 JAR(如果 JAR 被禁用",则不应由 JVM 加载/编译,不应被 Spring 等捡起)?

Is there a way to achieve this without too many awkard hacks? Or is recommended to utilize something like OSGi? Am I looking at this completely wrong and there is a better way to have JARs on the classpath that do not need always need loading (if the JAR is "disabled", it should not be loaded/compiled by the JVM, should not be picked up by Spring, etc.)?

推荐答案

如果在启动 Spring 应用程序之前复制 JAR 文件,这似乎是可能的.感觉很hackish,但它有效.使用风险自负!

It looks like this is possible if the JAR files are copied before starting the Spring application. It feels hackish, but it works. Use at your own risk!

您需要两个类,一个用于引导外部 JAR,然后将通过手动创建的 PropertiesLauncher 启动第二个类.引导类可以是普通的普通 Java 类(但也可以是 Spring Boot 应用程序),只有第二个类需要是 SpringBootApplication.

You need two classes, one for bootstrapping the external JARs, which will then start the second via a manually created PropertiesLauncher. The bootstrapping class can be a plain old regular Java class (but it can be a Spring Boot Application too) and only the second class needs to be a 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文件中,我们可以通过删除bootJar任务清单配置并通过springBoot应用设置来切换回默认的JarLauncher> 配置块.mainClass 将在 MANIFEST.MF 文件中作为 Start-Class 结束.

In the gradle file, we can switch back to the default JarLauncher, by removing the bootJar task manifest configuration and applying settings via the springBoot configuration block. mainClass will end up as Start-Class in the MANIFEST.MF file.

// build.gradle
springBoot {
    mainClass = 'com.example.customlauncher.BootstrapApplication'
}

在加载器的属性文件中,需要设置一个新的属性,指向真正的应用程序类.此文件中的设置仅由 PropertiesLauncher 选取并被 JarLauncher 忽略.换句话说:JarLauncher 委托给来自清单文件的 Start-ClassPropertiesLauncher 委托给 loader.main 来自它的属性文件.

In the properties file for the loader, a new property needs to be set, which points to the real application class. The settings in this file are only picked up by PropertiesLauncher and ignored by JarLauncher. In other words: JarLauncher delegates to Start-Class from the manifest file and PropertiesLauncher delegates to loader.main from its properties file.

# .../resources/loader.properties
loader.path=file:/path/to/dir
loader.main=com.example.customlauncher.Application

Spring (Boot) 将首先调用 BootstrapApplication 的 main 方法,如 MANIFEST.MF 文件中指定的(通过 springBoot 配置控制)build.gradle 文件中的块).在这个 main 方法的实现中,一个新的 PropertiesLauncher 被创建,主类设置为real".应用程序(即Application).

Spring (Boot) will first call the main method of BootstrapApplication, as specified in the MANIFEST.MF file (controlled via springBoot configuration block in the build.gradle file). In the implementation of this main method, a new PropertiesLauncher is created with the main class set to the "real" application (i.e. Application).

执行应用程序仍然通过相同的调用完成:

Executing the application is still done via the same invocation:

java -jar build/libs/customlauncher-0.0.1-SNAPSHOT.jar

在 JVM 启动之后添加到 /path/to/dir 的任何 JAR 文件,但之前在 BootstrapApplication 中调用 PropertiesLauncher#main 是然后在类路径和应用程序上下文中可用,如 Application 所示.

Any JAR files added to /path/to/dir after the JVM has started, but before calling PropertiesLauncher#main in BootstrapApplication are then available in the classpath and application context as seen from Application.

这篇关于如何在不重新启动 JVM 的情况下将外部 JAR 添加到 Spring 应用程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆