Spring @Async Annotation für asynchrones Verarbeiten

Die Spring @Async Annotation ermöglicht es uns, asynchrone Methoden in Spring zu erstellen. Lassen Sie uns @Async in diesem Tutorial über das Spring Framework erkunden. Wir werden unseren eigenen Service definieren und in diesem Beispiel Spring Boot 2 verwenden. Lassen Sie uns beginnen!

Spring @Async Annotation 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 werden:

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 Generate-Befehl auszuführen, da Maven alle erforderlichen Plugins und Artefakte herunterladen muss, um die Generierungsaufgabe zu erledigen.

Erstellung eines Projekts mit Maven

Sobald Sie das Projekt erstellt haben, können Sie es in Ihrer bevorzugten IDE öffnen. Der nächste Schritt ist das Hinzufügen der passenden 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/> 
</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>

Schließlich, um alle JARs zu verstehen, die dem Projekt hinzugefügt wurden, als wir diese Abhängigkeit hinzugefügt haben, können wir einen einfachen Maven-Befehl ausführen, der es uns ermöglicht, einen kompletten Abhängigkeitsbaum für ein Projekt zu sehen, wenn wir einige Abhängigkeiten hinzufügen. Hier ist ein Befehl, den wir verwenden können:

Aktivierung der Async-Unterstützung

Die Aktivierung der Async-Unterstützung ist ebenfalls nur eine Frage einer einzigen Annotation. Abgesehen von der Aktivierung der asynchronen Ausführung werden wir auch Executor verwenden, die es uns ermöglichen, Thread-Limits zu definieren. Mehr dazu, wenn 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 es Spring ermöglicht, asynchrone Methoden in einem Hintergrund-Thread-Pool auszuführen. Als nächstes fügen wir auch den erwähnten 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 haben wir festgelegt, dass maximal 2 Threads gleichzeitig ausgeführt werden sollen und die Warteschlangengröße auf 500 eingestellt 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 nutzt.

Spring @Async Annotation – Erstellung 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 getters and setters

    @Override
    public String toString() {
        return String.format("MovieModel{title='%s', producer='%s'}", title, producer);
    }
}

Wir haben @JsonIgnoreProperties verwendet, damit, wenn es mehr Attribute in der Antwort gibt, diese sicher von Spring ignoriert werden können.

Erstellung des Services

Es ist an der Zeit, unseren Service zu definieren, der die erwähnte Movie API aufrufen wird. Wir werden ein einfaches RestTemplate verwenden, um eine GET API zu treffen und Ergebnisse asynchron zu erhalten. Lassen Sie uns den Beispielcode betrachten, 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);
        // Artificial delay of 1s for demonstration purposes
        Thread.sleep(1000L);
        return CompletableFuture.completedFuture(results);
    }
}

Diese Klasse ist ein @Service, was sie für den Spring-Komponenten-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 zu Demonstrationszwecken hinzugefügt.

Spring @Async Annotation – Erstellung eines Command Line Runners

Wir werden unsere App mit einem CommandLineRunner ausführen, der der einfachste Weg ist, um unsere Anwendung zu testen. Ein CommandLineRunner wird unmittelbar nachdem alle Beans der Anwendung initialisiert wurden ausgeführt. Lassen Sie uns den Code für CommandLineRunner sehen:

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 {
        // Start the clock
        long start = System.currentTimeMillis();

        // Kick of multiple, asynchronous lookups
        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");

        // Join all threads so that we can wait until all are done
        CompletableFuture.allOf(page1, page2, page3).join();

        // Print results, including elapsed time        // Print results, including elapsed time
        LOG.info("Elapsed time: " + (System.currentTimeMillis() - start));
        LOG.info("--> " + page1.get());
        LOG.info("--> " + page2.get());
        LOG.info("--> " + page3.get());
    }
}

Wir haben gerade das RestTemplate verwendet, um die Beispiel-API, die wir verwendet haben, mit einigen zufällig ausgewählten Film-IDs zu treffen. Wir werden unsere Anwendung ausführen, um zu sehen, welche Ausgabe sie zeigt.

Spring @Async Annotation – Anwendung ausführen

Wenn wir die Anwendung ausführen, werden wir die folgende Ausgabe sehen:

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 gemacht, nämlich JDAsync-1 und JDAsync-2.

Fazit

In dieser Lektion haben wir untersucht, wie wir die asynchronen Fähigkeiten von Spring mit Spring Boot 2 Processing nutzen können – asynchrones Verarbeiten

Kostenlosen Account erstellen

Registrieren Sie sich jetzt und erhalten Sie Zugang zu unseren Cloud Produkten.

Das könnte Sie auch interessieren:

centron Managed Cloud Hosting in Deutschland

Mocking Void Methods mit EasyMock

Anleitung, JavaScript
Mocking Void Methods mit EasyMock Im Unit Testing ist Mocking eine essentielle Technik, die Entwicklern ermöglicht, das Verhalten komplexer Objekte zu simulieren. EasyMock ist ein beliebtes Framework zur Erstellung von…