Frühling @Async-Annotation ermöglicht es uns, asynchrone Methoden in Spring zu erstellen. Lassen Sie uns die @Async
-Annotation in diesem Tutorial zum Spring-Framework erkunden. Kurz gesagt, wenn wir eine Methode einer Bean mit der @Async
-Annotation annotieren, wird Spring sie in einem separaten Thread ausführen, und der Aufrufer der Methode wartet nicht, bis die Methode abgeschlossen ist. In diesem Beispiel werden wir unseren eigenen Service definieren und Spring Boot 2 verwenden. Lassen Sie uns beginnen!
Spring @Async Beispiel
Wir werden Maven verwenden, um ein Beispielprojekt für die Demonstration zu erstellen. Um das Projekt zu erstellen, führen Sie den folgenden Befehl in einem Verzeichnis aus, das Sie als Arbeitsbereich verwenden möchten:
mvn archetype:generate -DgroupId=com.journaldev.asynchmethods -DartifactId=JD-SpringBoot-AsyncMethods -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
Wenn Sie Maven zum ersten Mal ausführen, dauert es einige Sekunden, um den Generierungsbefehl abzuschließen, da Maven alle erforderlichen Plugins und Artefakte herunterladen muss, um die Generierungsaufgabe zu ermöglichen. So sieht die Projekt erstellung aus: Sobald Sie das Projekt erstellt haben, können Sie es in Ihrer Lieblings-IDE öffnen. Der nächste Schritt ist das Hinzufügen der entsprechenden Maven-Abhängigkeiten zum Projekt. Hier ist die
pom.xml
-Datei mit den entsprechenden Abhängigkeiten:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Zum Verständnis aller JARs, die dem Projekt hinzugefügt werden, wenn wir diese Abhängigkeit hinzufügen, können wir einen einfachen Maven-Befehl ausführen, der es uns ermöglicht, einen vollständigen Abhängigkeitsbaum für ein Projekt zu sehen, wenn wir einige Abhängigkeiten hinzufügen. Hier ist ein Befehl, den wir verwenden können:
mvn dependency:tree
Wenn wir diesen Befehl ausführen, zeigt er uns den folgenden Abhängigkeitsbaum:
Async-Support aktivieren
Das Aktivieren der Async-Unterstützung ist ebenfalls nur eine Frage einer einzigen Annotation. Neben der Aktivierung der Async-Ausführung werden wir auch einen Executor verwenden, der es uns ermöglicht, Thread-Limits festzulegen. Mehr dazu, sobald wir den Code schreiben:
package com.journaldev.asynchexample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@SpringBootApplication
@EnableAsync
public class AsyncApp {
...
}
Hier haben wir die @EnableAsync
-Annotation verwendet, die die Fähigkeit von Spring aktiviert, asynchrone Methoden in einem Hintergrund-Thread-Pool auszuführen. Als nächstes fügen wir den genannten Executor hinzu:
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("JDAsync-");
executor.initialize();
return executor;
}
Hier legen wir fest, dass maximal 2 Threads gleichzeitig ausgeführt werden sollen und die Warteschlangengröße auf 500 festgelegt ist. Hier ist der vollständige Code der Klasse mit Import-Anweisungen:
package com.journaldev.asynchexample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@SpringBootApplication
@EnableAsync
public class AsyncApp {
public static void main(String[] args) {
SpringApplication.run(AsyncApp.class, args).close();
}
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("JDAsync-");
executor.initialize();
return executor;
}
}
Als nächstes werden wir einen Service erstellen, der tatsächlich Thread-Ausführungen durchführt.
Erstellen eines Modells
Wir werden eine öffentliche Movie-API verwenden, die nur die Daten eines Films zurückgibt. Wir werden unser Modell dafür definieren:
package com.journaldev.asynchexample;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class MovieModel {
private String title;
private String producer;
// Standard-Getter und -Setter
@Override
public String toString() {
return String.format("MovieModel{title='%s', producer='%s'}", title, producer);
}
}
Wir haben @JsonIgnoreProperties
verwendet, damit bei einer größeren Anzahl von Attributen in der Antwort diese sicher von Spring ignoriert werden können.
Erstellen des Dienstes
Es ist an der Zeit, unseren Service zu definieren, der die genannte Movie API aufruft. Wir werden ein einfaches RestTemplate verwenden, um eine GET-API aufzurufen und Ergebnisse asynchron zu erhalten. Schauen wir uns den Beispielcode an, den wir verwenden:
package com.journaldev.asynchexample;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.CompletableFuture;
@Service
public class MovieService {
private static final Logger LOG = LoggerFactory.getLogger(MovieService.class);
private final RestTemplate restTemplate;
public MovieService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
@Async
public CompletableFuture lookForMovie(String movieId) throws InterruptedException {
LOG.info("Looking up Movie ID: {}", movieId);
String url = String.format("https://ghibliapi.herokuapp.com/films/%s", movieId);
MovieModel results = restTemplate.getForObject(url, MovieModel.class);
// Künstliche Verzögerung von 1s zu Demonstrationszwecken
Thread.sleep(1000L);
return CompletableFuture.completedFuture(results);
}
}
Diese Klasse ist ein @Service
, was sie für den Spring Component Scan qualifiziert. Der Rückgabetyp der Methode lookForMovie
ist CompletableFuture
, was eine Anforderung für jeden asynchronen Service ist. Da die Zeit für die API variieren kann, haben wir eine Verzögerung von 2 Sekunden für die Demonstration hinzugefügt.
Erstellung eines Command Line Runner
Wir werden unsere App mit einem CommandLineRunner ausführen, der der einfachste Weg ist, unsere Anwendung zu testen. Ein CommandLineRunner wird direkt nach der Initialisierung aller Beans der Anwendung ausgeführt. Schauen wir uns den Code für den CommandLineRunner an:
package com.journaldev.asynchexample;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Component
public class ApplicationRunner implements CommandLineRunner {
private static final Logger LOG = LoggerFactory.getLogger(ApplicationRunner.class);
private final MovieService movieService;
public ApplicationRunner(MovieService movieService) {
this.movieService = movieService;
}
@Override
public void run(String... args) throws Exception {
// Starte die Uhr
long start = System.currentTimeMillis();
// Starte mehrere asynchrone Abfragen
CompletableFuture page1 = movieService.lookForMovie("58611129-2dbc-4a81-a72f-77ddfc1b1b49");
CompletableFuture page2 = movieService.lookForMovie("2baf70d1-42bb-4437-b551-e5fed5a87abe");
CompletableFuture page3 = movieService.lookForMovie("4e236f34-b981-41c3-8c65-f8c9000b94e7");
// Warte, bis alle Threads fertig sind
CompletableFuture.allOf(page1, page2, page3).join();
// Ergebnisse anzeigen, einschließlich der vergangenen Zeit
LOG.info("Elapsed time: " + (System.currentTimeMillis() - start));
LOG.info("--> " + page1.get());
LOG.info("--> " + page2.get());
LOG.info("--> " + page3.get());
}
}
Wir haben einfach das RestTemplate verwendet, um die Beispiel-API mit einigen zufällig ausgewählten Movie-IDs aufzurufen. Wir werden unsere Anwendung ausführen, um zu sehen, welche Ausgabe sie zeigt.
Das Ausführen der Anwendung
Wenn wir die Anwendung ausführen, sehen wir die folgende Ausgabe:
2018-04-13 INFO 17868 --- [JDAsync-1] c.j.a.MovieService : Looking up Movie ID: 58611129-2dbc-4a81-a72f-77ddfc1b1b49
2018-04-13 08:00:09.518 INFO 17868 --- [JDAsync-2] c.j.a.MovieService : Looking up Movie ID: 2baf70d1-42bb-4437-b551-e5fed5a87abe
2018-04-13 08:00:12.254 INFO 17868 --- [JDAsync-1] c.j.a.MovieService : Looking up Movie ID: 4e236f34-b981-41c3-8c65-f8c9000b94e7
2018-04-13 08:00:13.565 INFO 17868 --- [main] c.j.a.ApplicationRunner : Elapsed time: 4056
2018-04-13 08:00:13.565 INFO 17868 --- [main] c.j.a.ApplicationRunner : --> MovieModel{title='My Neighbor Totoro', producer='Hayao Miyazaki'}
2018-04-13 08:00:13.565 INFO 17868 --- [main] c.j.a.ApplicationRunner : --> MovieModel{title='Castle in the Sky', producer='Isao Takahata'}
2018-04-13 08:00:13.566 INFO 17868 --- [main] c.j.a.ApplicationRunner : --> MovieModel{title='Only Yesterday', producer='Toshio Suzuki'}
Wenn Sie genau hinsehen, wurden nur zwei Threads zur Ausführung in der App erstellt, nämlich JDAsync-1
und JDAsync-2
.
Schlussfolgerung
In dieser Lektion haben wir untersucht, wie wir die asynchronen Fähigkeiten von Spring mit Spring Boot 2 nutzen können. Lesen Sie mehr Spring-bezogene Beiträge hier.
Quellcode herunterladen
Source:
https://www.digitalocean.com/community/tutorials/spring-async-annotation