Introduction
L'AWS SDK for Java v2 représente l'évolution majeure pour les applications Java scalables en 2026. Contrairement à la v1 synchrone, la v2 met l'accent sur les clients asynchrones (Netty), la pagination native et les transactions cross-services, essentiels pour des microservices performants. Ce tutoriel avancé cible les développeurs seniors : nous couvrons la configuration Maven, les credentials sécurisés via STS, les uploads S3 multipart >5GB, les transactions DynamoDB atomiques, la pagination EC2 avec Paginators et la gestion d'erreurs retryable. Chaque exemple est un snippet complet, testable en <5min avec un compte AWS gratuit. À la fin, vous bookmarkederez ce guide pour vos déploiements prod. (132 mots)
Prérequis
- Java 17+ (LTS recommandé pour GraalVM compatibilité)
- Maven 3.9+ ou Gradle 8+
- Compte AWS avec IAM user (clés API)
- AWS CLI installé pour tester (
aws configure) - IDE comme IntelliJ avec AWS Toolkit
- Connaissances avancées en Java (CompletableFuture, Streams)
Configuration Maven (pom.xml)
<?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>com.example</groupId>
<artifactId>aws-sdk-advanced</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<aws.sdk2.version>2.26.20</aws.sdk2.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>${aws.sdk2.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>ec2</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.13</version>
</dependency>
</dependencies>
</project>Ce pom.xml utilise le BOM pour gérer les versions SDK v2 cohérentes, évitant les conflits. Il inclut S3, DynamoDB, EC2 et STS pour assume-role. Compilez avec mvn compile ; piège : oubliez pas le BOM, sinon versions mismatch.
Gestion des credentials avancés
Pour la prod, évitez les clés hardcodées. Utilisez ~/.aws/credentials avec MFA ou assume-role via STS pour des sessions temporaires (15min-12h). La chaîne de providers SDK priorise : env vars > shared creds > IAM roles (EC2/Lambda).
Client STS AssumeRole asynchrone
package com.example;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sts.StsAsyncClient;
import software.amazon.awssdk.services.sts.model.AssumeRoleRequest;
import software.amazon.awssdk.services.sts.model.AssumeRoleResponse;
import software.amazon.awssdk.services.sts.model.Credentials;
import java.time.Duration;
public class StsAssumeRole {
public static void main(String[] args) {
StsAsyncClient stsClient = StsAsyncClient.builder()
.region(Region.EU_WEST_1)
.credentialsProvider(ProfileCredentialsProvider.create("dev-profile"))
.build();
AssumeRoleRequest request = AssumeRoleRequest.builder()
.roleArn("arn:aws:iam::123456789012:role/MyCrossAccountRole")
.roleSessionName("java-sdk-session")
.durationSeconds(3600)
.build();
stsClient.assumeRole(request).whenComplete((response, throwable) -> {
if (throwable != null) {
throwable.printStackTrace();
return;
}
Credentials creds = response.credentials();
System.out.println("AccessKey: " + creds.accessKeyId());
System.out.println("Expires: " + creds.expirationTime());
}).join();
stsClient.close();
}
}Ce code assume un rôle cross-account de façon asynchrone avec CompletableFuture. Utilisez ces creds temporaires pour d'autres clients. Piège : région STS doit matcher ; testez avec aws sts assume-role d'abord.
S3 : Upload multipart avancé
Analogie : Comme diviser un camion en remorques pour une autoroute saturée. Pour fichiers >5GB, multipart est obligatoire (100 parts max, 5MB-5GB/part). Nous créons un upload complet avec metadata et tagging.
S3 Multipart Upload complet
package com.example;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class S3MultipartUpload {
public static void main(String[] args) throws IOException {
S3Client s3 = S3Client.builder().region(Region.EU_WEST_1).build();
String bucket = "my-advanced-bucket";
String key = "large-file.zip";
File file = new File("path/to/largefile.zip"); // >5GB
// Create multipart upload
CreateMultipartUploadResponse createRes = s3.createMultipartUpload(CreateMultipartUploadRequest.builder()
.bucket(bucket)
.key(key)
.metadata(m -> m.put("app", "java-sdk-v2"))
.tagging("env=prod&priority=high")
.build());
String uploadId = createRes.uploadId();
List<CompletedPart> parts = new ArrayList<>();
int partNumber = 1;
long fileSize = file.length();
long partSize = 5 * 1024 * 1024; // 5MB
byte[] fileContent = Files.readAllBytes(file.toPath());
for (long pos = 0; pos < fileSize; pos += partSize) {
long size = Math.min(partSize, fileSize - pos);
UploadPartResponse partRes = s3.uploadPart(UploadPartRequest.builder()
.bucket(bucket)
.key(key)
.uploadId(uploadId)
.partNumber(partNumber)
.contentLength(size)
.build(), RequestBody.fromBytes(fileContent, (int)pos, (int)size));
parts.add(CompletedPart.builder().eTag(partRes.eTag()).partNumber(partNumber).build());
partNumber++;
}
// Complete
s3.completeMultipartUpload(CompletedMultipartUploadRequest.builder()
.bucket(bucket)
.key(key)
.uploadId(uploadId)
.parts(parts)
.build());
s3.close();
System.out.println("Upload complet!");
}
}Code synchrone pour simplicité, mais passez à S3AsyncClient pour prod. Gère metadata/tags auto-appliqués. Piège : sortez parts par partNumber ascendant ; abortez avec AbortMultipartUpload si échec.
DynamoDB : Transactions atomiques
Les TransactWriteItems garantissent ACID cross-tables (100 ops max). Idéal pour e-commerce (débit stock + commande). Utilisez conditions pour éviter race conditions.
DynamoDB TransactWriteItems
package com.example;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;
import java.util.Arrays;
public class DynamoDBTransactions {
public static void main(String[] args) {
DynamoDbClient dynamoDb = DynamoDbClient.builder().region(Region.EU_WEST_1).build();
String tableOrders = "Orders";
String tableInventory = "Inventory";
TransactWriteItem orderItem = TransactWriteItem.builder()
.put(Put.builder().tableName(tableOrders)
.item(m -> m.attribute("orderId", AttributeValue.builder().s("ORD-123").build())
.attribute("productId", AttributeValue.builder().s("PROD-456").build())
.attribute("quantity", AttributeValue.builder().n("2").build()))
.build())
.build();
TransactWriteItem inventoryItem = TransactWriteItem.builder()
.update(Update.builder().tableName(tableInventory)
.key(k -> k.attribute("productId", AttributeValue.builder().s("PROD-456").build()))
.updateExpression("ADD quantity :dec")
.expressionAttributeValues(m -> m.put(":dec", AttributeValue.builder().n("2").build()))
.conditionExpression("quantity >= :dec")
.build())
.build();
try {
dynamoDb.transactWriteItems(TransactWriteItemsRequest.builder()
.transactItems(Arrays.asList(orderItem, inventoryItem))
.build());
System.out.println("Transaction réussie!");
} catch (TransactionCanceledException e) {
System.out.println("Annulée: " + e.cancellationReasons());
}
dynamoDb.close();
}
}Transaction atomique : commande + update stock avec condition. Si stock insuffisant, tout rollback. Piège : limitez à 4MB total ; utilisez Batch pour non-atomique.
Pagination EC2 avec Paginators
Avancé : Pour >1000 instances, utilisez paginator() pour itérer sans NextToken manuelle. Flux réactif compatible avec Reactor/WebFlux.
EC2 Pagination avancée
package com.example;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest;
import software.amazon.awssdk.services.ec2.model.DescribeInstancesResponse;
import software.amazon.awssdk.services.ec2.paginators.DescribeInstancesPublisher;
import java.util.concurrent.CompletableFuture;
public class EC2Pagination {
public static void main(String[] args) {
Ec2Client ec2 = Ec2Client.builder().region(Region.EU_WEST_1).build();
DescribeInstancesPublisher publisher = ec2.paginator(DescribeInstancesRequest.builder().maxResults(50).build());
CompletableFuture<Void> future = publisher.subscribe(res -> {
res.reservations().forEach(reservation ->
reservation.instances().forEach(instance ->
System.out.println("Instance: " + instance.instanceId())));
});
future.join();
ec2.close();
}
}Publisher itère automatiquement sur pages (Publisher/Subscriber pattern). Async par défaut. Piège : maxResults trop haut throttle ; filirez avec .filter() sur Flux si Spring.
Bonnes pratiques
- Clients singleton : Réutilisez un client par région/app via builder.cache()
- Async partout : Préférez AsyncClient pour I/O non-bloquant (>10x perf)
- Retry policy : Configurez SdkDefaultClientExceptionRetryClassifier pour 503/429
- Logging structuré : Activez AWS_XRAY pour traces distribuées
- Régions multi : Utilisez Region.of() dynamique par service
Erreurs courantes à éviter
- Credentials chain incomplète : Vérifiez
AWS_PROFILEet~/.aws/config; testez avecaws sts get-caller-identity - Pagination manuelle : Oublie NextToken → résultats tronqués ; toujours paginator()
- Multipart sans complete : Coûts storage orphelins ; wrappez en try-finally
- ConditionCheck fail : TransactionCanceledException sans log reasons()
Pour aller plus loin
- Docs officielles : AWS SDK Java v2
- Exemples GitHub : awsdocs/aws-doc-sdk-examples
- Formation avancée : Learni Group Formations AWS
- Migrez vers v3 preview pour GraalVM native