引言
在上一篇博客中,我们详细探讨了AWS Simple Email Service(SES)的入门知识和配置步骤。本篇博客将聚焦于如何在Java应用程序中集成AWS SES SDK v2,以实现电子邮件的发送功能。我们将逐步引导您完成必要的设置、编写代码,并讨论如何优化发送过程。
一、准备Java开发环境
首先,确保您的开发环境中已安装了Java开发工具包(JDK)。对于AWS SDK v2,推荐使用JDK 8或更高版本。您还需要配置构建工具,如Maven或Gradle,以简化依赖管理和项目构建过程。
二、添加AWS SDK v2依赖
在您的Java项目中,需要引入AWS SDK v2的依赖。以Maven为例,在项目的pom.xml
文件中添加以下依赖项:
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>ses</artifactId>
<version>2.x.x</version> <!-- 替换为最新的SDK版本 -->
</dependency>
</dependencies>
对于Gradle用户,在build.gradle
文件中添加类似的依赖项。
三、配置AWS凭证
在使用AWS SDK v2之前,您需要配置AWS凭证。有几种方法可以实现这一点,其中最常用的是使用环境变量或AWS凭证文件。
- 环境变量:设置
AWS_ACCESS_KEY_ID
和AWS_SECRET_ACCESS_KEY
环境变量,分别包含您的AWS访问密钥和秘密访问密钥。 - AWS凭证文件:在您的主目录下创建一个名为
.aws/credentials
的文件,并添加以下内容:
[default]
aws_access_key_id = 您的访问密钥
aws_secret_access_key = 您的秘密访问密钥
确保将占位符替换为您的实际凭证。
四、编写Java代码发送电子邮件
现在,我们可以开始编写Java代码来发送电子邮件了。以下是一个简单的示例,演示了如何使用AWS SDK v2发送一封文本电子邮件:
import com.alibaba.fastjson.JSON;
import com.linzi.pitpat.base.constants.RedisConstants;
import com.linzi.pitpat.base.enums.DingTalkEnum;
import com.linzi.pitpat.base.utils.DingTalkUtils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.errors.ServiceUnavailableException;
import org.slf4j.MDC;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ses.SesClient;
import software.amazon.awssdk.services.ses.model.*;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* @author xx
* @date 2024/2/27 14:55
*/
@Component
@Slf4j
public class EmailSender {
private static final String ACCESS_KEY = "xx";
private static final String SECRET_KEY = "xx";
private static final Region REGION = Region.US_WEST_2; // e.g., "us-west-2"
private static final String FROM_ADDRESS = "xx@xx.com";
private static final String configurationSetName = "xx";//配置集名称
private static final int MAX_RETRIES = 3; // 最大重试次数
private static final int AWS_SES_SEND_QUOTA_TIME = 5 * 60;
@Resource(name = "taskExecutor")
private ThreadPoolTaskExecutor executor;
@Resource
private RedisTemplate redisTemplate;
private SesClient sesClient;
public EmailSender() {
this.sesClient = SesClient.builder()
.region(REGION)
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(ACCESS_KEY, SECRET_KEY)))
.build();
}
/**
* 发送电子邮件
*
* @param toAddresses 接收者电子邮件地址列表
* @param subject 邮件主题
* @param bodyHtml 邮件HTML正文
* @param bodyText 邮件文本正文(可选)
* @return SendEmailResponse 响应对象
*/
public SendEmailResponse sendEmail(List<String> toAddresses, String subject, String bodyHtml, String bodyText) {
try {
SendEmailRequest sendEmailRequest = createSendEmailRequest(toAddresses,subject,bodyHtml,bodyText);
SendEmailResponse sendEmailResponse = null;
int retryCount = 0;
while (retryCount < MAX_RETRIES) {
try {
sendEmailResponse = sesClient.sendEmail(sendEmailRequest);
log.info("sendEmail res={}", sendEmailResponse);
break;
} catch (SdkException e) {
if (!(e.getCause() instanceof ServiceUnavailableException)) {
throw e;
}
log.info("Retrying due to service unavailable", e);
retryCount++;
}
}
return sendEmailResponse;
} catch (SdkException e) {
handleSendEmailException(e,toAddresses);
close();
throw e;
}
}
/**
* 异步发送电子邮件
*
* @param toAddresses 接收者电子邮件地址列表
* @param subject 邮件主题
* @param bodyHtml 邮件HTML正文
* @param bodyText 邮件文本正文(可选)
* @return SendEmailResponse 响应对象
*/
public Future<Boolean> sendEmailAsync(List<String> toAddresses, String subject, String bodyHtml, String bodyText) {
SendEmailRequest sendEmailRequest = createSendEmailRequest(toAddresses,subject,bodyHtml,bodyText);
return executor.submit(() -> {
try {
int retryCount = 0;
while (retryCount < MAX_RETRIES) {
try {
SendEmailResponse sendEmailResponse = sesClient.sendEmail(sendEmailRequest);
log.info("sendEmail res={}" , sendEmailResponse);
break;
} catch (SdkException e) {
if (!(e.getCause() instanceof ServiceUnavailableException)) {
throw e;
}
log.info("Retrying due to service unavailable", e);
retryCount++;
}
}
return true;
} catch (SdkException e) {
handleSendEmailException(e, toAddresses);
close();
return false;
}
});
}
/**
* 检查是否有剩余发送配额
* 接近最大配额又不超过
* @return
*/
public boolean checkSendQuota() {
GetSendQuotaResponse quotaResponse = sesClient.getSendQuota();
// 打印配额信息
log.info("Max 24-hour send rate: " + quotaResponse.max24HourSend());
log.info("Max send rate: " + quotaResponse.maxSendRate());
log.info("Sent last hour: " + quotaResponse.sentLast24Hours());
// 计算剩余配额(这里只计算了24小时内的剩余配额)
double remainingQuota24Hours = quotaResponse.max24HourSend() - quotaResponse.sentLast24Hours();
log.info("Remaining quota for 24 hours: " + remainingQuota24Hours);
//发送量接近当前的配额不再使用,当前配置剩余3不再使用
if (remainingQuota24Hours <= 3) {
return false;
}
return true;
}
private SendEmailRequest createSendEmailRequest(List<String> toAddresses, String subject, String bodyHtml, String bodyText) {
return SendEmailRequest.builder()
.destination(Destination.builder().toAddresses(toAddresses).build())
.message(Message.builder()
.body(Body.builder()
.html(Content.builder().data(bodyHtml).build())
.text(bodyText != null ? Content.builder().data(bodyText).build() : null)
.build())
.subject(Content.builder().data(subject).build())
.build())
.source(FROM_ADDRESS)
.configurationSetName(configurationSetName)
.build();
}
/**
* 处理发送邮件异常
*
* @param e 异常对象
* @param toAddresses
*/
private void handleSendEmailException(SdkException e, List<String> toAddresses) {
// 这里可以添加自定义的日志记录或异常处理逻辑
log.info("handleSendEmailException error,error=",e);
// 其他业务处理
}
/**
* 关闭SesClient资源
*/
public void close() {
try {
sesClient.close();
} catch (Exception e) {
log.error("Failed to close SES client", e);
}
}
// Example usage:
public static void main(String[] args) {
EmailSender sender = new EmailSender();
try {
// 示例:发送邮件
List<String> toAddresses = List.of("xx@xx.com","xx@xx.com");
String subject = "Test Email Subject";
String bodyHtml = "<h1>Hello, this is a test email!</h1>";
String bodyText = "Hello, this is a test email!";
SendEmailResponse response = sender.sendEmail(toAddresses, subject, bodyHtml, bodyText);
System.out.println(response);
log.info("res="+ response.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
请确保将示例代码中的占位符替换为您的实际值。这段代码创建了一个SesClient
实例,构建了电子邮件的各个部分(发件人、收件人、主题和正文),然后发送了电子邮件。如果发送成功,它将打印出邮件的ID;否则,将打印异常堆栈跟踪。
五、处理异常和错误
在发送电子邮件时,可能会遇到各种异常和错误,如网络问题、凭证无效或收件人地址不存在等。为了处理这些情况,您应该在代码中添加适当的异常处理逻辑。使用try-catch块捕获SesClient
抛出的异常,并根据需要进行处理。您还可以考虑使用日志记录库(如Log4j或SLF4J)来记录错误信息,以便稍后进行分析和调试。
六、优化和扩展
一旦您成功集成了AWS SES并发送了电子邮件,就可以考虑如何优化和扩展您的实现。以下是一些建议:
- 批量发送:如果您需要发送大量电子邮件,可以考虑使用AWS SES的批量发送功能来提高效率。通过构建包含多个收件人的单个请求,您可以一次性发送多封电子邮件。
- 使用模板:AWS SES支持使用模板来创建具有一致外观和格式的电子邮件。您可以创建模板并在发送电子邮件时引用它们,从而简化代码并提高可维护性。
- 监控和日志记录:为了跟踪电子邮件的发送情况并识别潜在问题,请确保实施适当的监控和日志记录策略。您可以利用AWS CloudWatch等工具来监控SES的发送统计信息和错误指标。
- 安全性最佳实践:始终遵循安全性最佳实践,如使用强凭证、定期轮换密钥以及限制对SES API的访问权限。此外,确保您的应用程序能够处理敏感数据并遵守相关法规要求。
结语
通过本篇博客,您已经了解了如何在Java应用程序中集成AWS SES SDK v2来发送电子邮件。从准备开发环境到编写代码、处理异常和错误以及优化和扩展实现,我们逐步引导您完成了整个过程。现在,您可以将这些知识应用于实际项目中,并利用AWS SES的强大功能来发送事务性、营销性或批量电子邮件了。祝您编码愉快!