기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.
에서 실행 인터셉터 사용 AWS SDK for Java 2.x
요청 및 응답 수명 주기에 연결된 AWS SDK for Java 2.x 후크의 실행 인터셉터는 API 호출 실행의 다양한 단계에서 사용자 지정 로직을 수행합니다. 인터셉터를 사용하여 로깅, 지표 수집, 요청 수정, 디버깅 및 오류 처리와 같은 교차 커팅 문제를 구현합니다.
인터셉터는 다양한 요청 실행 단계에 대한 후크를 제공하는 ExecutionInterceptor
인터셉터 수명 주기
ExecutionInterceptor
인터페이스는 요청 실행 중에 특정 시점에서 호출되는 메서드를 제공합니다.
beforeExecution
- 요청이 실행되기 전에 호출됨modifyRequest
- SDK 요청 객체를 수정합니다.beforeMarshalling
- HTTP에 대한 요청이 마샬링되기 전에 호출됩니다.afterMarshalling
- HTTP에 대한 요청이 마샬링된 후 호출됩니다.modifyHttpRequest
- HTTP 요청을 수정합니다.beforeTransmission
- HTTP 요청이 전송되기 전에 호출됩니다.afterTransmission
- HTTP 응답을 수신한 후 호출됩니다.modifyHttpResponse
- HTTP 응답을 수정합니다.beforeUnmarshalling
- HTTP 응답이 마샬링 해제되기 전에 호출됩니다.afterUnmarshalling
- HTTP 응답이 마샬링 해제된 후 호출됩니다.modifyResponse
- SDK 응답 객체를 수정합니다.afterExecution
- 요청 실행 성공 후 호출됨onExecutionFailure
- 요청 실행이 실패할 때 호출됩니다.
인터셉터 등록
overrideConfiguration
메서드를 사용하여 서비스 클라이언트를 빌드할 때 인터셉터를 등록합니다. 여러 인터셉터를 등록할 수 있으며 등록한 순서대로 실행됩니다.
// 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();
인터셉터 예제
다음 클래스는 실행 인터셉터를 사용하여 핵심 비즈니스 로직을 변경하지 않고 S3 작업에 로깅, 성능 모니터링 및 요청 수정과 같은 교차 커팅 문제를 추가하는 방법을 보여줍니다.
이 예제에서는 S3 클라이언트에 여러 인터셉터를 등록하고 실제 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>
모범 사례
-
인터셉터를 가볍게 유지 - 인터셉터는 모든 요청에 대해 실행되므로 성능에 영향을 미칠 수 있는 과도한 계산 또는 차단 작업을 피하세요.
-
예외 정상 처리 - 인터셉터에서 예외가 발생하면 전체 요청이 실패합니다. 잠재적으로 장애가 발생할 수 있는 작업에는 항상 try-catch 블록을 사용합니다.
-
순서 중요 - 가로채기는 등록한 순서대로 실행됩니다. 인터셉터를 등록할 때 인터셉터 간의 종속성을 고려합니다.
-
상태에 ExecutionAttributes 사용 - 서로 다른 인터셉터 메서드 간에 데이터를 전달해야 하는 경우 인스턴스 변수
ExecutionAttributes
대신를 사용하여 스레드 안전을 보장합니다. -
민감한 데이터에 유의 - 요청 및 응답을 로깅할 때 자격 증명 또는 개인 데이터와 같은 민감한 정보를 로깅하지 않도록 주의하십시오.
컨텍스트 객체
각 인터셉터 메서드는 다양한 실행 단계에서 요청 및 응답 정보에 대한 액세스를 제공하는 컨텍스트 객체를 수신합니다.
-
Context.BeforeExecution
- 원래 SDK 요청에 대한 액세스 권한을 제공합니다. -
Context.ModifyRequest
- SDK 요청을 수정합니다. -
Context.ModifyHttpRequest
- HTTP 요청을 수정합니다. -
Context.AfterExecution
- 요청 및 응답 모두에 대한 액세스 권한을 제공합니다. -
Context.FailedExecution
- 요청 및 발생한 예외에 대한 액세스 권한을 제공합니다.