Use interceptores de execução no AWS SDK for Java 2.x - AWS SDK for Java 2.x

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Use interceptores de execução no AWS SDK for Java 2.x

Interceptores de execução inseridos AWS SDK for Java 2.x no ciclo de vida da solicitação e da resposta para executar a lógica personalizada em vários estágios da execução da chamada de API. Use interceptores para implementar questões transversais, como registro, coleta de métricas, modificação de solicitações, depuração e tratamento de erros.

Os interceptores implementam a ExecutionInterceptorinterface, que fornece ganchos para diferentes fases da execução da solicitação.

Ciclo de vida do interceptor

A ExecutionInterceptor interface fornece métodos que são chamados em pontos específicos durante a execução da solicitação:

  • beforeExecution- Chamado antes da execução da solicitação

  • modifyRequest- Modifica o objeto de solicitação do SDK

  • beforeMarshalling- Chamado antes da solicitação ser enviada para HTTP

  • afterMarshalling- Chamado após a solicitação ser enviada para HTTP

  • modifyHttpRequest- Modifica a solicitação HTTP

  • beforeTransmission- Chamado antes do envio da solicitação HTTP

  • afterTransmission- Chamado após o recebimento da resposta HTTP

  • modifyHttpResponse- Modifica a resposta HTTP

  • beforeUnmarshalling- Chamado antes que a resposta HTTP seja desorganizada

  • afterUnmarshalling- Chamado após a desorganização da resposta HTTP

  • modifyResponse- Modifica o objeto de resposta do SDK

  • afterExecution- Chamado após a execução bem-sucedida da solicitação

  • onExecutionFailure- Chamado quando a execução da solicitação falha

Registre interceptores

Registre interceptores ao criar um cliente de serviço usando o overrideConfiguration método. Você pode registrar vários interceptores e eles são executados na ordem em que você os registra.

// 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();

Exemplo de interceptor

A classe a seguir demonstra como usar interceptores de execução para adicionar questões transversais, como registro, monitoramento de desempenho e modificação de solicitações, às suas operações do S3 sem alterar sua lógica comercial principal.

Este exemplo mostra como registrar vários interceptores em um cliente S3 e vê-los em ação durante chamadas reais de 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>

Práticas recomendadas

  • Mantenha os interceptores leves: os interceptores são executados para cada solicitação, portanto, evite cálculos pesados ou operações de bloqueio que possam afetar o desempenho.

  • Lide com as exceções normalmente - Se o interceptor lançar uma exceção, isso fará com que toda a solicitação falhe. Sempre use blocos try-catch para operações potencialmente falhadas.

  • A ordem é importante - Os interceptores são executados na ordem em que você os registra. Considere as dependências entre seus interceptores ao registrá-los.

  • Use ExecutionAttributes para estado - Se você precisar passar dados entre diferentes métodos interceptores, use variáveis ExecutionAttributes em vez de instância para garantir a segurança do encadeamento.

  • Cuidado com dados confidenciais - Ao registrar solicitações e respostas, tenha cuidado para não registrar informações confidenciais, como credenciais ou dados pessoais.

Objetos de contexto

Cada método interceptor recebe um objeto de contexto que fornece acesso às informações de solicitação e resposta em diferentes estágios de execução:

  • Context.BeforeExecution- Fornece acesso à solicitação original do SDK

  • Context.ModifyRequest- Modifica a solicitação do SDK

  • Context.ModifyHttpRequest- Modifica a solicitação HTTP

  • Context.AfterExecution- Fornece acesso à solicitação e à resposta

  • Context.FailedExecution- Fornece acesso à solicitação e à exceção que ocorreu