将多个CommandLineRunner/ApplicationRunner实现类放入Spring Boot可执行的jar中,并通过启动时的属性来切换执行的类
简要概述
我发现在需要创建一个简单的批处理程序时,使用Spring Boot实现CommandLineRunner/ApplicationRunner接口的类是简单且方便的。所以我尝试创建了几个批处理程序,
-
- バッチをいくつも作りたい場合、バッチ1つに付き実行可能 jar ファイルを1つ作らないといけないのだろうか?
- 一部のクラスや実行可能 jar に含まれる lib ディレクトリの下の外部ライブラリの jar ファイルを外に出して共通化しようと思ったが -jar オプションと -classpath オプションが共存できないらしく、jar ファイルからクラスや外部ライブラリの jar ファイルを外に出して共通化する方法が分からない。
在这方面我一直感到困扰。毕竟,我不想为每个批处理创建一个可执行的jar文件,所以我考虑了将实现了CommandLineRunner/ApplicationRunner接口的类都放入一个可执行的jar文件,并在启动时使用属性进行切换的方法。
如何切换执行的类?
-
- Spring Boot では CommandLineRunner/ApplicationRunner インターフェースを実装したクラスの Bean が生成されていればその run メソッドが実行されるので、実行時に指定するプロパティの値で Bean を生成するのか否かを制御します。
- Bean 生成の制御は Spring Boot で提供されている @ConditionalOnProperty アノテーションを利用します。このアノテーションを @Bean アノテーションと一緒にメソッドに付けておくと、指定されたプロパティと値の組み合わせがある場合のみ Bean が生成されるようになります。
在尝试时的开发环境中,使用的是Spring Boot的版本。
我正在以下环境中进行测试。
-
- JDK 8u102
-
- IntelliJ IDEA Ultimate 2016.2
- Spring Boot 1.4
样品项目的规范
创建一个名为 multi-applicationrunner-sample-0.0.1-SNAPSHOT.jar 的可执行 jar 文件,并指定实现了 CommandLineRunner/ApplicationRunner 接口的类来运行它,可以使用 -Dbatch.execute=… 参数来执行它。
在实际中会以以下方式工作(还会输出Spring Boot的标志和INFO日志)。
> java -Dbatch.execute=Sample01 -jar multi-applicationrunner-sample-0.0.1-SNAPSHOT.jar
Sample01バッチが起動しました
> java -Dbatch.execute=Sample02 -jar multi-applicationrunner-sample-0.0.1-SNAPSHOT.jar
Sample02バッチです
样本项目的包文件结构
样例项目是在Spring Initializr中创建的Gradle项目。除了以下文件外,还有build.gradle文件。
src
└ main
├ java
│ └ ksbysample
│ └ batch
│ ├ MultiApplicationRunnerSampleApplication
│ ├ PrintService.java
│ ├ Sample01BatchRunner.java
│ └ Sample02BatchRunner.java
└ resources
各个文件的内容
构建.gradle
这还是使用 Spring Initializr 生成的。
buildscript {
ext {
springBootVersion = '1.4.0.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'
jar {
baseName = 'multi-applicationrunner-sample'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}
多应用运行器示例应用程序.java
这个也是保持了原始的 Spring Initializr 生成的状态。
package ksbysample.batch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MultiApplicationRunnerSampleApplication {
public static void main(String[] args) {
SpringApplication.run(MultiApplicationRunnerSampleApplication.class, args);
}
}
PrintService.java 打印服务.java
为了验证即使在Sample01BatchRunner和Sample02BatchRunner类中没有添加@Component注解,PrintService类也可以使用@Autowired注解来进行描述,创建了这个类。
package ksbysample.batch;
import org.springframework.stereotype.Service;
@Service
public class PrintService {
public void println(String msg) {
System.out.println(msg);
}
}
样例01BatchRunner.java
package ksbysample.batch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class Sample01BatchRunner implements ApplicationRunner {
@Autowired
private PrintService printService;
@Override
public void run(ApplicationArguments args) throws Exception {
printService.println("Sample01バッチが起動しました");
}
@Configuration
public static class Sample01BatchConfig {
@Bean
@ConditionalOnProperty(value = { "batch.execute" }, havingValue = "Sample01")
public Sample01BatchRunner sample01BatchRunner() {
return new Sample01BatchRunner();
}
}
}
重要之处在于,
-
- Sample01BatchRunner クラスは ApplicationRunner インターフェース(あるいは CommandLineRunner インターフェース)を実装し、run メソッドを override します。
-
- Sample01BatchRunner クラス自体には @Component アノテーションを付加しません。
-
- Sample01BatchRunner クラス内に @Autowired アノテーションを付加したフィールドは記述可能です。Bean として生成される時にインスタンスが DI されます。
@Configuration アノテーションを付加した Sample01BatchConfig クラスを記述します。@Configuration アノテーションを付加しているのはこのクラスの内部で @Bean アノテーションを付加したメソッドを定義するためです。
Sample01BatchConfig クラス内で Sample01BatchRunner Bean を生成するためのメソッドを定義します。このメソッドには @ConditionalOnProperty アノテーションを付加し、起動時のプロパティで Bean が生成されるか否かを制御されるようにします。
Sample02BatchRunner.java的中文本地化版本
package ksbysample.batch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class Sample02BatchRunner implements ApplicationRunner {
@Autowired
private PrintService printService;
@Override
public void run(ApplicationArguments args) throws Exception {
printService.println("Sample02バッチです");
}
@Configuration
public static class Sample02BatchConfig {
@Bean
@ConditionalOnProperty(value = { "batch.execute" }, havingValue = "Sample02")
public Sample02BatchRunner sample01BatchRunner() {
return new Sample02BatchRunner();
}
}
}