Ich folgte dem Tutorial auf der offiziellen Seite, um das Projekt zu erstellen, und führte Spring Batch von IntelliJ IDEA aus. Zusätzlich zur Möglichkeit, die Funktionen einer normalen Spring Boot-Anwendung zu verwenden, bietet sie Jobausführungsergebnisse, Wiederholungsfunktionen usw., und ich war der Meinung, dass die Qualität als Batch-Framework hoch war. Wenn ich jedoch die Hauptklasse der Anwendung von IntelliJ aus ausführe, werden alle definierten Jobs ausgeführt, und ich wusste nicht, wie nur der angegebene Job ausgeführt werden soll, daher werde ich dies untersuchen. Insbesondere.
Als Voraussetzung für Batch-Anwendungen wollte ich mindestens Folgendes erreichen.
Da es sich bei der vorhandenen Anwendung um ein Transaktionsskript handelt und es kompliziert geworden ist, erwägen wir die Einführung von Spring Batch. Da die Art und Weise, wie eine vorhandene Anwendung ausgeführt wird, der später beschriebenen Schnittstelle von [CommandLineJobRunner](Implementierung mit #CommandLineJobRunner) ähnelt, habe ich überlegt, sie hier zu implementieren, aber diese Methode konnte die Anforderung nicht erfüllen. Ich konnte die Anforderungen jedoch erfüllen, indem ich von JobLauncherCommandLineRunner aus startete. Die Verzeichnisstruktur ist für verschiedene Dateien wie folgt.
├── build.gradle
└── src
└── main
├── java
│ └── hello
│ ├── Application.java
│ ├── BatchConfiguration.java
│ ├── JobCompletionNotificationListener.java
│ ├── Person.java
│ └── PersonItemProcessor.java
└── resources
├── application-development.properties
├── application-production.properties
├── application.properties
├── log4jdbc.log4j2.properties
├── sample-data.csv
└── schema-all.sql
build.gradle Die folgenden Abhängigkeiten wurden hinzugefügt, um die Verbindung zu MySQL zu erzwingen.
runtime("mysql:mysql-connector-java")
compile "org.lazyluke:log4jdbc-remix:0.2.7"
compile("org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16")
application.properties Bei Verwendung der Spring Boot-Funktion werden die dem angegebenen Profil zugeordneten application- $ {profile} .properties getrennt von den übergeordneten application.properties in das Framework geladen und die Umgebungsinformationen in das Environment-Objekt geladen. Es ist auf (: //docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/Environment.html) festgelegt. Die Umgebungsinformationen werden von Datasource spring.datasource. * benötigt Features-Connect-to-Production-Datenbank) können enthalten sein.
appliaction-production.properties
spring.datasource.username=production_user
spring.datasource.password=production_password
spring.datasource.url=jdbc:log4jdbc:mysql://kasakaid.production.jp:3306/kasakaidDB?useSSL=true
spring.datasource.driver-class-name=net.sf.log4jdbc.DriverSpy
appliaction-development.properties
spring.datasource.username=root
spring.datasource.password=mysql
spring.datasource.url=jdbc:log4jdbc:mysql://127.0.0.1:3306/kasakaidDB?useSSL=false
spring.datasource.driver-class-name=net.sf.log4jdbc.DriverSpy
Wie oben erwähnt, können Sie, wenn Sie application.properties für jedes Profil vorbereiten, die Datenbank so wechseln, dass sie eine Verbindung mit der Umgebungsnummer oder dem Argument der Befehlszeile herstellt.
__ 07.09.2019 Nachtrag __
Spring Batch schreibt die Ergebnisse der Jobausführung, z. B. dieser Job war erfolgreich oder fehlgeschlagen, in das in Spring Batch integrierte Datenbankschema. Es werden mehrere DDLs zum Erstellen dieses Schemas bereitgestellt, um verschiedene Datenbanken wie MySQL und PostgreSQL aufzunehmen. Dies sind jedoch die build.gradle des Spring Batch-Projekts. Beim Erstellen mit /spring-projects/spring-batch/blob/547533fab072289c062916e51c589de36ea3dfe2/spring-batch-core/build.gradle) diese Datei /547533fab072289c062916e51c589de36ea3dfe2/spring-batch-core/src/main/sql/schema.sql.vpp) scheint als Vorlage generiert zu sein. Das Verhalten beim Erstellen eines Schemas finden Sie auf der offiziellen Seite.
If you use Spring Batch, it comes pre-packaged with SQL initialization scripts for most popular database platforms. Spring Boot can detect your database type and execute those scripts on startup. If you use an embedded database, this happens by default. You can also enable it for any database type, as shown in the following example:
spring.batch.initialize-schema=always
Wie Sie sehen können, können Sie festlegen, dass es immer ausgeführt wird, wenn Spring Batch gestartet wird. In Umgebungen, in denen Anwendungen kontinuierlich ausgeführt werden, einschließlich kommerzieller Umgebungen, ist es jedoch besser, diese Schemaerstellungsoperation nicht immer auszuführen, wenn Spring Batch gestartet wird. Dies liegt daran, dass der Stapelprozess erfolgreich ist, wenn Spring Batch gestartet wird. Wenn diese Schemaerstellungsoperation ausgeführt wird, wurde das Schema bereits generiert, sodass beim Erstellen des Schemas immer ein Fehler ausgegeben wird. Es scheint, dass es in der aktuellen Version keine Einstellung zum Ändern des Verhaltens gibt, die besagt: "Wenn das Schema erstellt wird, tun Sie nichts". Da dieser Fehler Rauschen verursacht, gibt es keine Möglichkeit, dies zu tun. Erstellen Sie daher im Voraus ein Schema für Spring Batch in der entsprechenden Umgebung. Die DDL selbst kann bestätigt werden, indem Sie unter org.springframework.batch.core in org.springframework.batch: spring-batch-core:
Nach dem Erstellen des Schemas müssen Sie das Schema nicht erstellen, insbesondere wenn Spring Batch gestartet wird. Legen Sie daher keine fest, damit das Schema nicht in einer kommerziellen Umgebung erstellt wird.
application-production.properties
spring.batch.initialize-schema=none
Auf der anderen Seite sollten während des Tests normalerweise flüchtige In-Memory-Datenbanken wie H2 verwendet werden. Erstellen Sie daher während des Tests unbedingt ein Schema. (Wenn Sie jedoch den Spring Batch-Mechanismus nicht verwenden und nur aus der Service-Klasse usw. testen, müssen Sie kein bestimmtes Schema erstellen.)
application-test.properties
spring.batch.initialize-schema=always
log4jdbc.log4j2.properties Dies ist die Einstellungsdatei, die für die Verwendung von [log4jdbc] erforderlich ist (http://log4jdbc.brunorozendo.com/). Nicht im Zusammenhang mit Spring Batch, aber beschrieben, um die Anforderungen zu erfüllen.
log4jdbc.log4j2.properties
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
Wählen Sie nach dem Ausführen der obigen Konfiguration beim Ausführen der Spring Batch-Anwendung zwei [Systemeigenschaften] aus (http://itdoc.hitachi.co.jp/manuals/link/cosmi_v0950/03Y3020D/EY300058.HTM). Angeben.
No | Systemeigenschaften | Erläuterung |
---|---|---|
1 | spring.profiles.active | Profil zum Aktivieren |
2 | spring.batch.job.names | Der Name des auszuführenden Jobs |
Das erste spring.profiles.active ist das zu aktivierende Profil. Der zweite spring.batch.job.names Sie können den Namen des Jobs angeben, mit dem ausgeführt werden soll.
Führen Sie den Stapel mit Systemeigenschaften aus. Systemeigenschaften können als Laufzeitargumente angegeben oder als Umgebungsvariablen festgelegt werden. Im folgenden Beispiel werden zwei Systemeigenschaften als Laufzeitargumente ausgeführt.
java -Dspring.profiles.active=development -Dspring.batch.job.names=importUserJob -jar gs-batch-processing-0.1.0.jar
Sie können auf MySQL zugreifen, indem Sie Ihr Entwicklungsprofil angeben! Natürlich wird auch die ASCII-Kunst von Spring Boot angezeigt.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)
2019-01-20 23:16:58.028 INFO 86837 --- [ main] hello.Application : Starting Application on sakaidaikki-no-MacBook-Air.local with PID 86837 (/Users/kasakaid/dev/java/gs-batch-processing/complete/build/libs/gs-batch-processing-0.1.0.jar started by kasakaid in /Users/kasakaid/dev/java/gs-batch-processing/complete/build/libs)
2019-01-20 23:16:58.034 INFO 86837 --- [ main] hello.Application : The following profiles are active: development
2019-01-20 23:17:03.570 INFO 86837 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2019-01-20 23:24:25.447 INFO 87366 --- [ main] o.s.b.c.r.s.JobRepositoryFactoryBean : No database type set, using meta data indicating: MYSQL
2019-01-20 23:24:25.906 INFO 87366 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
2019-01-20 23:24:25.934 INFO 87366 --- [ main] jdbc.audit : 1. Connection.getMetaData() returned com.mysql.jdbc.JDBC4DatabaseMetaData@14cd1699
2019-01-20 23:24:25.935 INFO 87366 --- [ main] jdbc.audit : 1. Connection.clearWarnings() returned
2019-01-20 23:24:25.942 INFO 87366 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executing SQL script from class path resource [org/springframework/batch/core/schema-mysql.sql]
Wenn Sie auf der offiziellen Seite die CommandLineJobRunner-Klasse verwenden, können Sie den Job mit den folgenden Argumenten angeben. Es gibt.
No | Streit | Erläuterung |
---|---|---|
1 | jobPath | The location of the XML file that will be used to create an ApplicationContext. This file should contain everything needed to run the complete Job |
2 | jobName | The name of the job to be run. |
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date(date)=2007/05/05
Wenn ein Stapel mit dieser Methode realisiert wird, muss die Klasse mit der Hauptfunktion, die zuerst ausgeführt werden soll, die CommandLineJobRunner-Klasse sein. Ändern Sie daher den Taskabschnitt buildJar von build.graldle, um die Startklasse von der Standardklasse hello.Application in CommandLineJobRunner zu ändern.
bootJar {
baseName = 'gs-batch-processing'
version = '0.1.0'
manifest {
attributes 'Start-Class': 'org.springframework.batch.core.launch.support.CommandLineJobRunner'
}
}
Wenn Sie die bootJar-Task in diesem Status ausführen, wird unter build / libs eine JAR generiert. Entpacken Sie die JAR-Datei und suchen Sie META in JAR-Dateispezifikationen. -Wenn Sie INF / MANIFEST.MF aktivieren, können Sie sehen, dass die Änderungen in build.gradle übernommen werden. Die Hauptklasse (https://docs.oracle.com/javase/tutorial/deployment/jar/appman.html) gibt weiterhin org.springframework.boot.loader.JarLauncher an. Und die Spring Boot-Spezifikation Start-Class Ist hallo.Application standardmäßig, ändert sich aber zu CommandLineJobRunner.
MANIFEST.MF
Manifest-Version: 1.0
Start-Class: org.springframework.batch.core.launch.support.CommandLineJobRunner
Main-Class: org.springframework.boot.loader.JarLauncher
Sie haben die Klasse mit der Hauptfunktion in der JAR-Datei erfolgreich festgelegt. Wenn Sie dieses JAR ausführen, wird zuerst die CommandLineJobRunner-Hauptfunktion gestartet. Führen Sie mit dem generierten jar aus, das im Argument von -jar angegeben ist.
java -Dspring.profiles.active=development -jar gs-batch-processing-0.1.0.jar hello.BatchConfiguration importUserJob
Als Systemeigenschaft ist weiterhin ein Profil erforderlich, aber der in spring.batch.job.names angegebene Jobname wird als erstes Argument als jobPath angegeben. Das zweite Argument gibt den in @ Bean
definierten importUserJob an. Ich dachte, es würde normal enden, aber als ich die Anwendung ausführte, bekam ich den folgenden Fehler.
21:12:52.267 [main] ERROR org.springframework.batch.core.launch.support.CommandLineJobRunner - Job Terminated in error: Error creating bean with name 'writer' defined in hello.BatchConfiguration: Unsatisfied dependency expressed through method 'writer' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'writer' defined in hello.BatchConfiguration: Unsatisfied dependency expressed through method 'writer' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732)
In der im Beispiel enthaltenen Definition des Writers wird eine dataSource mit dataSource als Argument angegeben, aber ich denke, dass das Argument 0 ist.
BatchConfiguration.java
@Bean
public JdbcBatchItemWriter<Person> writer(DataSource dataSource) {
return new JdbcBatchItemWriterBuilder<Person>()
.itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
.sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
.dataSource(dataSource)
.build();
}
Ich habe mich gefragt, was damit passiert ist, aber die BatchConfiguration-Klasse enthält nur zwei Anmerkungen:
@EnableBatchProcessing
@Configuration
Ich glaube nicht, dass es eine Definition für Spring Boot gibt, daher habe ich die Anmerkung von "@ Configuration" in ["@ SpringBootApplication"] geändert (https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot -using-springbootapplication-annotation.html). Das liegt daran, dass "@ SpringBootApplication" auch "@ Configuration" enthält.
BatchConfiguration.java
@SpringBootApplication
@EnableBatchProcessing
public class BatchConfiguration {
Dies wird normal abgeschlossen.
Wenn ich jedoch nach Ausführung des Stapelverarbeitungsprozesses in die Datenbank schaue, gibt es keine Daten, einschließlich der Personentabelle. Wenn Sie sich das Ausführungsergebnisprotokoll ansehen, scheinen Sie auf hsql zuzugreifen, das sich im Arbeitsspeicher befindet. Übrigens erscheint auch die Zeichenfolge der ASCII-Grafik von Spring Boot nicht.
21:30:30.720 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.hsqldb.jdbc.JDBCConnection@2f8bc07b
21:30:30.724 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'hikariPoolDataSourceMetadataProvider'
21:30:30.724 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'hikariPoolDataSourceMetadataProvider'
21:30:32.546 [main] INFO org.springframework.batch.core.repository.support.JobRepositoryFactoryBean - No database type set, using meta data indicating: HSQL
21:30:33.032 [main] INFO org.springframework.jdbc.datasource.init.ScriptUtils - Executing SQL script from class path resource [org/springframework/batch/core/schema-hsqldb.sql]
21:30:33.044 [main] INFO org.springframework.jdbc.datasource.init.ScriptUtils - Executed SQL script from class path resource [org/springframework/batch/core/schema-hsqldb.sql] in 12 ms.
21:30:33.753 [HikariPool-1 connection closer] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Closing connection org.hsqldb.jdbc.JDBCConnection@17776a8: (connection evicted)
Ich fragte mich, warum das so war, und verglich die CommandLineJobRunner-Klasse mit der SpringApplication-Klasse. Was ich hier gefunden habe, ist, dass CommandLineJobRunner nur einen Spring-Kontext mit AnnotationConfigApplicationContext erstellt. Infolgedessen wird die Bean im Container registriert, es wird jedoch kein anderes Verhalten erwartet.
CommandLineJobRunner.java
int start(String jobPath, String jobIdentifier, String[] parameters, Set<String> opts) {
ConfigurableApplicationContext context = null;
try {
try {
context = new AnnotationConfigApplicationContext(Class.forName(jobPath));
} catch (ClassNotFoundException cnfe) {
context = new ClassPathXmlApplicationContext(jobPath);
}
Andererseits werden in Spring Application die Funktionen für die Umgebung initialisiert. Es scheint auch, dass die Spring Boot ASCII-Zeichenfolge von der printBanner-Methode in der initialisierten Umgebungsinstanz generiert wird.
SplingApplicaton.java
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments); //Hier werden die Umgebungseigenschaften festgelegt. Anwendung.Der Wert von Eigenschaften wird in der DataSourceProperties-Klasse und in der Datenquelle festgelegt.
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
In der SpringApplication-Klasse wird der Job nach Abschluss verschiedener Initialisierungen schließlich von der JobLauncherCommandLineRunner-Klasse ausgeführt. Da in der CommandLineJobRunner-Klasse kein SpringApplication-Initialisierungsprozess vorhanden ist, können die profilbezogenen Funktionen nicht verwendet werden. Diese Forschung konzentrierte sich auf die Profilfunktion, um die Anforderungen zu erfüllen, aber es kann andere Funktionen geben, die nicht verwendet werden können.
@ PropertySource
Es stellt sich heraus, dass application.properties, das standardmäßig geladen werden sollte, nicht verfügbar ist, geschweige denn die Funktionalität des Profils. Verwenden Sie daher unter Verwendung von @ PropertySource
die Eigenschaftendatei Ich habe überlegt, eine Richtlinie explizit anzugeben.
BatchConfiguartion.java
@SpringBootApplication
@EnableBatchProcessing
@PropertySource("application.properties")
public class BatchConfiguration {
}
Fügen Sie dann die Umgebung am Ende des Schlüssels application.properties hinzu.
application.properties
spring.batch.initialize-schema=ALWAYS
spring.datasource.username.development=root
spring.datasource.password.development=mysql
spring.datasource.url.development=jdbc:log4jdbc:mysql://127.0.0.1:3306/kasakaidDB?useSSL=false
spring.datasource.driver-class-name.development=net.sf.log4jdbc.DriverSpy
spring.datasource.username.production=production_user
spring.datasource.password.production=production_password
spring.datasource.url.production=jdbc:log4jdbc:mysql://production.kasakaid.io:3306/kasakaidDB?useSSL=false
spring.datasource.driver-class-name.production=net.sf.log4jdbc.DriverSpy
Sie sollten mit System.getEnv ("spring.profiles.active") auf die Systemeigenschaften zugreifen können. Ich denke, dass die Informationen über die Umgebung dadurch unterschieden werden können, aber ich denke, dass diese Methode sehr kompliziert ist.
Eine große Anzahl ähnlicher Tasten ist aneinandergereiht und schwer zu unterscheiden
Mithilfe der Spring Boot-Funktion ermittelt Spring automatisch die entsprechenden application.properties basierend auf Ihrem Profil, um Ihre aktuelle Umgebung zu bestimmen. Bei dieser Methode muss der Implementierer jedoch immer wissen, wie die Umgebung ist. Die Vorteile der Verwendung des Frameworks gehen stark verloren. Die Implementierung ist so.
SpringUtils.java
@Component
public SpringUtils {
@Autowired
Environment env;
public String property(String key) {
return env.getProperty(key + "." + System.getEnv("spring.profiles.active"));
}
}
Dieses Mal kommt plötzlich die Einstellung der Eigenschaften von Datasource heraus. In Spring Boot wird der Wert von spring.datasource. * In application.properties automatisch in Datasource festgelegt. Bei dieser Methode müssen Sie jedoch den Wert von Datasource explizit festlegen.
Configuration.java
@Autowired
SpringUtils springUtils;
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setDriverClassName(springUtils.property("spring.datasource.driver-class-name"));
ds.setUsername(springUtils.property("spring.datasource.username"));
ds.setPassword(springUtils.property("spring.datasource.password"));
ds.setJdbcUrl(springUtils.property("spring.datasource.url"));
return ds;
}
Wie oben erwähnt, habe ich die Nachteile einschließlich der Vorstellungskraft aufgelistet. Es sind drei Inhalte aufgeführt, aber es gibt ein Problem, bei dem der Implementierer über Dinge nachdenken muss, die nicht berücksichtigt werden sollten. Darüber hinaus werden im Verlauf der Entwicklung weitere Unannehmlichkeiten auftreten, und es gibt keine Garantie dafür, dass die zu berücksichtigenden Faktoren kollidieren und fatale Unannehmlichkeiten verursachen. Aus diesem Grund kam ich auf die Idee, dass es besser ist, die Ausführung von Spring Batch mit CommandLineJobRunner zu beenden.
Recommended Posts