Utilisez des intercepteurs d'exécution dans AWS SDK for Java 2.x - AWS SDK for Java 2.x

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Utilisez des intercepteurs d'exécution dans AWS SDK for Java 2.x

Des intercepteurs d'exécution intégrés au AWS SDK for Java 2.x cycle de vie des demandes et des réponses pour exécuter une logique personnalisée aux différentes étapes de l'exécution des appels d'API. Utilisez des intercepteurs pour mettre en œuvre des préoccupations transversales telles que la journalisation, la collecte de métriques, la modification des demandes, le débogage et la gestion des erreurs.

Les intercepteurs implémentent l'ExecutionInterceptorinterface, qui fournit des crochets pour les différentes phases d'exécution des requêtes.

Cycle de vie de l'intercepteur

L'ExecutionInterceptorinterface fournit des méthodes qui sont appelées à des moments spécifiques lors de l'exécution de la demande :

  • beforeExecution- Appelé avant l'exécution de la demande

  • modifyRequest- Modifie l'objet de demande du SDK

  • beforeMarshalling- Appelé avant que la requête ne soit acheminée vers HTTP

  • afterMarshalling- Appelé après le transfert de la requête vers HTTP

  • modifyHttpRequest- Modifie la requête HTTP

  • beforeTransmission- Appelé avant l'envoi de la requête HTTP

  • afterTransmission- Appelé après réception de la réponse HTTP

  • modifyHttpResponse- Modifie la réponse HTTP

  • beforeUnmarshalling- Appelé avant que la réponse HTTP ne soit désactivée

  • afterUnmarshalling- Appelé après le débranchement de la réponse HTTP

  • modifyResponse- Modifie l'objet de réponse du SDK

  • afterExecution- Appelé après une exécution réussie de la demande

  • onExecutionFailure- Appelé en cas d'échec de l'exécution de la demande

Enregistrer les intercepteurs

Enregistrez les intercepteurs lorsque vous créez un client de service à l'aide de overrideConfiguration cette méthode. Vous pouvez enregistrer plusieurs intercepteurs, et ils s'exécutent dans l'ordre dans lequel vous les avez enregistrés.

// Register a single interceptor. SqsClient client = SqsClient.builder() .overrideConfiguration(c -> c.addExecutionInterceptor(new LoggingInterceptor())) .build(); // Register multiple interceptors. S3Client s3Client = S3Client.builder() .overrideConfiguration(config -> config .addExecutionInterceptor(new TimingInterceptor()) .addExecutionInterceptor(new LoggingInterceptor()) .addExecutionInterceptor(new RequestModificationInterceptor())) .build();

Exemple d'intercepteur

La classe suivante montre comment utiliser les intercepteurs d'exécution pour ajouter des préoccupations transversales telles que la journalisation, le suivi des performances et la modification des demandes à vos opérations S3 sans modifier votre logique métier principale.

Cet exemple vous montre comment enregistrer plusieurs intercepteurs sur un client S3 et les voir en action lors de véritables appels d' AWS API.

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.awscore.exception.AwsServiceException; import software.amazon.awssdk.core.ApiName; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.SdkHttpResponse; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.HeadBucketRequest; import software.amazon.awssdk.services.s3.model.ListBucketsResponse; import java.time.Duration; import java.time.Instant;
public class S3InterceptorsDemo { private static final Logger logger = LoggerFactory.getLogger(S3InterceptorsDemo.class); public static void main(String[] args) { logger.info("=== AWS SDK for Java v2 - S3 Interceptors Demo ==="); // Create an S3 client with multiple interceptors. S3Client s3Client = S3Client.builder() .overrideConfiguration(config -> config .addExecutionInterceptor(new TimingInterceptor()) .addExecutionInterceptor(new LoggingInterceptor()) .addExecutionInterceptor(new RequestModificationInterceptor())) .build(); try { logger.info("🚀 Starting S3 operations with interceptors..."); // Operation 1: List buckets. logger.info("📋 Operation 1: Listing S3 buckets"); logger.info("----------------------------------------"); ListBucketsResponse listBucketsResponse = s3Client.listBuckets(); logger.info("✅ Found {} buckets", listBucketsResponse.buckets().size()); // Operation 2: Try to access a bucket that likely doesn't exist. logger.info("🔍 Operation 2: Checking non-existent bucket (demonstrating error interceptor)"); logger.info("----------------------------------------"); try { s3Client.headBucket(HeadBucketRequest.builder() .bucket("amzn-s3-demo-bucket-that-does-not-exist-1234") .build()); } catch (Exception e) { logger.info("Expected error occurred (interceptor should have logged it)"); } } catch (Exception e) { logger.error("❌ Error during S3 operations: {}", e.getMessage(), e); } finally { s3Client.close(); logger.info("🔚 Demo completed - S3 client closed"); } } // Logging interceptor. private static class LoggingInterceptor implements ExecutionInterceptor { private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class); @Override public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { logger.info("🔄 [LOGGING] Starting request: {}", context.request().getClass().getSimpleName()); } @Override public void afterExecution(Context.AfterExecution context, ExecutionAttributes executionAttributes) { logger.info("✅ [LOGGING] Completed request: {}", context.request().getClass().getSimpleName()); } @Override public void onExecutionFailure(Context.FailedExecution context, ExecutionAttributes executionAttributes) { logger.error("❌ [LOGGING] Request failed: {}", context.request().getClass().getSimpleName()); if (context.exception() instanceof AwsServiceException) { AwsServiceException ase = (AwsServiceException) context.exception(); if (ase.awsErrorDetails().errorCode() != null) { SdkHttpResponse httpResponse = ase.awsErrorDetails().sdkHttpResponse(); logger.error(" HTTP Status: {}", httpResponse.statusCode()); logger.error(" Error Code: {}", ase.awsErrorDetails().errorCode()); logger.error(" Error Message: {}", ase.awsErrorDetails().errorMessage()); } } } } // Performance timing interceptor. private static class TimingInterceptor implements ExecutionInterceptor { private static final Logger logger = LoggerFactory.getLogger(TimingInterceptor.class); private Instant startTime; @Override public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { startTime = Instant.now(); logger.info("⏱️ [TIMING] Request started at: {}", startTime); } @Override public void afterExecution(Context.AfterExecution context, ExecutionAttributes executionAttributes) { if (startTime != null) { Duration duration = Duration.between(startTime, Instant.now()); logger.info("⏱️ [TIMING] Request completed in: {}ms", duration.toMillis()); } } @Override public void onExecutionFailure(Context.FailedExecution context, ExecutionAttributes executionAttributes) { if (startTime != null) { Duration duration = Duration.between(startTime, Instant.now()); logger.warn("⏱️ [TIMING] Request failed after: {}ms", duration.toMillis()); } } } // Request modification interceptor private static class RequestModificationInterceptor implements ExecutionInterceptor { private static final Logger logger = LoggerFactory.getLogger(RequestModificationInterceptor.class); @Override public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttributes executionAttributes) { SdkRequest originalRequest = context.request(); logger.info("🔧 [MODIFY] Modifying request: {}", originalRequest.getClass().getSimpleName()); // For ListBucketsRequest, we can't modify much since it has no settable properties // For HeadBucketRequest, we can demonstrate modifying the request if (originalRequest instanceof HeadBucketRequest) { HeadBucketRequest headRequest = (HeadBucketRequest) originalRequest; // Create a new request with an API name added. return HeadBucketRequest.builder() .bucket(headRequest.bucket()) .overrideConfiguration(b -> b.addApiName(ApiName.builder() .name("My-API") .version("1.0") .build())) .build(); } logger.info("Not a HeadBucketRequest, returning original request"); return originalRequest; } @Override public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) { logger.info("🔧 [MODIFY] Adding custom HTTP headers"); return context.httpRequest().toBuilder() .putHeader("X-Custom-Header", "S3InterceptorDemo") .putHeader("X-Request-ID", java.util.UUID.randomUUID().toString()) .build(); } } }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>interceptors-examples</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <name>interceptors-examples</name> <description>Demonstration of execution interceptors in AWS SDK for Java v2</description> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <aws.java.sdk.version>2.31.62</aws.java.sdk.version> <exec.mainClass>org.example.S3InterceptorsDemo</exec.mainClass> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>bom</artifactId> <version>${aws.java.sdk.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-bom</artifactId> <version>2.23.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.13</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-1.2-api</artifactId> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.10.1</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- Compiler Plugin --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>17</source> <target>17</target> </configuration> </plugin> <!-- Surefire Plugin for running tests --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.2</version> </plugin> <!-- Exec Plugin for running the main class --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.1.0</version> <configuration> <mainClass>${exec.mainClass}</mainClass> </configuration> </plugin> <!-- Shade Plugin to create executable JAR --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>${exec.mainClass}</mainClass> </transformer> </transformers> <createDependencyReducedPom>false</createDependencyReducedPom> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>

Bonnes pratiques

  • Conservez la légèreté des intercepteurs : les intercepteurs s'exécutent pour chaque requête, afin d'éviter les calculs lourds ou les opérations de blocage susceptibles d'avoir un impact sur les performances.

  • Gérez les exceptions avec élégance : si votre intercepteur lance une exception, l'ensemble de la demande échoue. Utilisez toujours des blocs try-catch en cas d'échec potentiel des opérations.

  • L'ordre compte : les intercepteurs exécutent dans l'ordre dans lequel vous les avez enregistrés. Tenez compte des dépendances entre vos intercepteurs lorsque vous les enregistrez.

  • Utiliser ExecutionAttributes pour l'état : si vous devez transmettre des données entre différentes méthodes d'interception, utilisez ExecutionAttributes plutôt des variables d'instance pour garantir la sécurité des threads.

  • Soyez attentif aux données sensibles - Lorsque vous enregistrez des demandes et des réponses, veillez à ne pas enregistrer d'informations sensibles telles que les informations d'identification ou les données personnelles.

Objets contextuels

Chaque méthode d'interception reçoit un objet de contexte qui permet d'accéder aux informations de demande et de réponse à différents stades d'exécution :

  • Context.BeforeExecution- Permet d'accéder à la demande du SDK d'origine

  • Context.ModifyRequest- Modifie la demande du SDK

  • Context.ModifyHttpRequest- Modifie la requête HTTP

  • Context.AfterExecution- Permet d'accéder à la fois à la demande et à la réponse

  • Context.FailedExecution- Permet d'accéder à la demande et à l'exception survenue