aws-doc-sdk-examples:用 Spring Boot 构建基于 Amazon RDS for MySQL 的工作项跟踪 Web 应用 示例工程教程后端【免费下载链接】aws-doc-sdk-examplesWelcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below.项目地址https://gitcode.com/gh_mirrors/aw/aws-doc-sdk-examples点击查看免费下载本篇基于仓库中 javav2/usecases/Creating_rds_item_tracker/README.md 的完整教程讲解如何构建一个“React 单页应用 Spring REST API Amazon RDS for MySQL”的解耦 Web 应用Spring 层通过 JDBC 对 MySQL 的Work表做增删改查通过 AWS SDK for Java (v2) 的 Secrets Manager 客户端安全获取数据库凭据并通过 Amazon SES 将动态生成的 Excel 报表以附件形式邮件发送。读完并动手复现后你将掌握跨服务RDS Secrets Manager SES的 Spring Boot 实战模式、JDBC 预编译语句的 CRUD 写法以及 SDK v2 的sendRawEmail带附件邮件发送。1. 应用目标与用户能力该示例属于javav2/usecases目录下的多服务cross-service实战案例面向中级开发者要求掌握 Java、Maven 与 JavaScript。用户通过 React 前端基于 Cloudscape 组件库的 SPA可以查看全部active活跃工作项查看全部archived已归档工作项新增一个工作项将某个活跃项转为归档项点击 Archive 按钮输入收件人邮箱并点击 Send report把活跃项汇总成的 Excel 报表通过 Amazon SES 发出。前后端解耦关系为React SPA 通过 RESTful GET/POST/PUT 请求调用 Spring REST APISpring REST API 使用 Java JDBC API 对 Amazon RDS for MySQL 执行 CRUD再以 HTTP 响应返回 JSON。2. 前置条件一个 AWS 账号教程所用服务均在 AWS Free Tier 范围内但运行代码可能产生费用完成后务必清理资源Java IDE教程使用 IntelliJJava JDK 17Maven 3.6 或更高版本完成 SDK for Java 开发环境凭据链配置。注意本代码未在全部 AWS 区域测试部分服务仅在特定区域可用。3. 创建 AWS 资源3.1 Amazon RDS for MySQL 与 Work 表创建一个 Amazon RDS for MySQL 数据库实例其中包含名为Work的表主键为idwork字段定义如下字段类型说明idworkVARCHAR(45)主键PKdateDATE工作项创建日期descriptionVARCHAR(400)工作项描述guideVARCHAR(45)正在进行的交付物statusVARCHAR(400)状态描述usernameVARCHAR(45)录入该工作项的用户archiveTINYINT(4)是否已归档0活跃1归档创建实例后在控制台的Connectivity security区域记录Endpoint与Port本例端口为 3306后续 JDBC 连接会用到。同时必须为数据库的安全组配置入站规则放行你开发环境所在 IP否则无法从 MySQL Workbench 等客户端连入。3.2 用 AWS Secrets Manager 存储数据库凭据不要把数据库凭据硬编码进 Java 逻辑。教程要求在 Secrets Manager 中创建名为itemtracker/mysql的 Secret其值为一个 JSON 字符串包含username、password、host三个键这一结构直接对应仓库中的反序列化目标类 User.javapublic class User { private String username; private String password; private String host; String getUsername() { return this.username; } String getPassword() { return this.password; } String getHost() { return this.host; } }DatabaseService会调用 SDK v2 的GetSecretValue拿到secretString()再用 Gson 反序列化成User从而得到连接三要素。4. 创建 IntelliJ 项目与 POM 依赖在 IntelliJ 中新建 Maven 项目Project SDK 选17GroupId 填aws-springArtifactId 填ItemTrackerMySQLRest。仓库中实际提交的 POM 位于 pom.xmlgroupId为org.example、artifactId为ItemTrackerMySQLRest、版本1.0-SNAPSHOT其关键配置与依赖如下建议按此对齐依赖版本作用spring-boot-starter-parentparent2.6.4Spring Boot 2.x 父 POM提供 starter 依赖管理software.amazon.awssdk:bomdependencyManagementimport2.21.20统一管理 AWS SDK v2 各服务客户端版本software.amazon.awssdk:secretsmanager由 BOM 管理Secrets Manager 客户端getSecretValuesoftware.amazon.awssdk:ses由 BOM 管理SES 客户端sendRawEmailmysql:mysql-connector-javaruntime由 Spring Boot 父 POM 管理MySQL JDBC 驱动com.google.code.gson:gson2.10.1解析 Secrets Manager 返回的 JSON 与响应net.sourceforge.jexcelapi:jxl2.6.10动态生成 .xls 报表commons-io:commons-io2.7IOUtils.toByteArray读取附件字节jakarta.mail:jakarta.mail-api/com.sun.mail:jakarta.mail2.0.1 / 1.6.8构造 MIME 多部分邮件spring-boot-starter-web由父 POM 管理REST 控制器与内嵌 Tomcat默认 8080 端口parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.6.4/version /parent properties java.version17/java.version maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target /properties dependencyManagement dependencies dependency groupIdsoftware.amazon.awssdk/groupId artifactIdbom/artifactId version2.21.20/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement注意rdsdata也在依赖列表中但从源码结构看本应用走的是直接 JDBC 连接而非 RDS Data API实际参与数据访问的是secretsmanager与ses两个客户端。5. Java 类逐一解析com.aws.rest 包所有类位于 src/main/java/com/aws/rest共 8 个文件。5.1 AppSpring Boot 入口package com.aws.rest; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class App { public static void main(String[] args) throws Throwable { SpringApplication.run(App.class, args); } }SpringBootApplication隐含了组件扫描、自动装配与配置属性绑定能力这也是后续Component标注的DatabaseService、WriteExcel能被自动注入的前提。仓库版本见 App.java。5.2 ConnectionHelperJDBC 连接封装ConnectionHelper.java 负责建立和关闭与 RDS for MySQL 的连接public class ConnectionHelper { public static Connection getConnection(String host, String user, String password) throws SQLException { try { Class.forName(com.mysql.cj.jdbc.Driver); String url2 jdbc:mysql:// host :3306/mydb?useSSLfalse; return DriverManager.getConnection(url2, user, password); } catch (SQLException | ClassNotFoundException e) { e.printStackTrace(); } return null; } public static void close(Connection connection) { try { if (connection ! null) { connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } }实现要点驱动类为 MySQL Connector/J 8 的com.mysql.cj.jdbc.DriverURL 中的端口 3306与库名 mydb是硬编码的host来自 Secrets Manager复现时若库名/端口不同需自行调整useSSLfalse仅适合开发联调生产场景应启用 TLS该方法失败时返回null调用方DatabaseService用assert c ! null兜底——这是教程级写法生产代码应抛出受检异常。5.3 DatabaseServiceSecrets Manager JDBC 的核心DatabaseService.java 是应用与 AWS 服务的交汇点包含四类方法。1构造 Secrets Manager 客户端并取秘密值源码第 29–48 行private SecretsManagerClient getSecretClient() { Region region Region.US_WEST_2; return SecretsManagerClient.builder() .region(region) .credentialsProvider(ProfileCredentialsProvider.create()) .build(); } private String getSecretValues() { SecretsManagerClient secretClient getSecretClient(); String secretName itemtracker/mysql; GetSecretValueRequest valueRequest GetSecretValueRequest.builder() .secretId(secretName) .build(); GetSecretValueResponse valueResponse secretClient.getSecretValue(valueRequest); return valueResponse.secretString(); }注意两处必须与你的 AWS 环境对齐的硬编码Region 固定为US_WEST_2Secret 所在的区域凭据来源为ProfileCredentialsProvider即本地~/.aws/credentials的默认 profile。Secret 名itemtracker/mysql与第 3.2 节创建的秘密一致。2查询getItemsDataSQLReport(int flag)。flag是三态查询开关配合PreparedStatement参数化查询flagSQL语义0Select ... FROM work where username? and archive?参数(user, 0)仅活跃项1同上参数(user, 1)仅归档项其他如 -1Select ... FROM work全表全部项值得注意的实现细节活跃/归档分支中username被硬编码为user意味着报表实际只统计该用户名名下的记录结果集按列序 1–7 逐字段映射为WorkItemidwork, username, date, description, guide, status, archive。3归档flipItemArchive(String id)query update work set archive ? where idwork id ; PreparedStatement updateForm c.prepareStatement(query); updateForm.setBoolean(1, true); updateForm.execute();4新增injestNewSubmission(WorkItem item)。生成UUID作为idwork把当前时间格式化为yyyy/MM/dd HH:mm:ss再解析为java.sql.Date只保留日期部分随后执行全参数化的INSERT INTO work (idwork, username, date, description, guide, status, archive) VALUES(?,?,?,?,?,?,?)archive初始为false。三个数据方法都遵循同一资源管理范式try内建连与执行、finally中调用ConnectionHelper.close(c)释放连接。5.4 MainControllerREST 路由MainController.java 使用RequestMapping(api/items)作为基路径并用CrossOrigin(origins *)放开跨域React 前端开发服务器与 8080 端口同源策略不同GetMapping() public ListWorkItem getItems(RequestParam(required false) String archived) { IterableWorkItem result; if (archived ! null archived.compareTo(false) 0) result dbService.getItemsDataSQLReport(0); else if (archived ! null archived.compareTo(true) 0) result dbService.getItemsDataSQLReport(1); else result dbService.getItemsDataSQLReport(-1); return StreamSupport.stream(result.spliterator(), false) .collect(Collectors.toUnmodifiableList()); } PutMapping({id}:archive) public String modUser(PathVariable String id) { dbService.flipItemArchive(id); return id was archived; } PostMapping() public ListWorkItem addItem(RequestBody MapString, String payload) { // 从 payload 取 name/guide/description/status构造 WorkItem // 生成 UUID 与当前时间调用 dbService.injestNewSubmission(item) // 再返回 getItemsDataSQLReport(0) 的活跃项列表 }三个路由的设计GET /api/itemsarchived查询参数缺省时返回全量flag-1?archivedfalse映射 flag0?archivedtrue映射 flag1PUT /api/items/{id}:archive路径中的冒号是一种“自定义方法”风格代码注释中引用了 Google API 设计文档的 custom methods 惯例语义是“把该 id 的项归档”POST /api/items请求体为 JSON 对象name、guide、description、status插入成功后再次查询活跃项并整体返回便于前端一次响应完成“新增 刷新列表”。5.5 ReportController报表邮件入口ReportController.java 路由为api/items:report注意这里是类级路径而非子路径处理POST请求PostMapping() public String sendReport(RequestBody MapString, String body) { ListWorkItem list dbService.getItemsDataSQLReport(0); // 仅活跃项 try { InputStream is writeExcel.write(list); // 生成 Excel 字节流 sm.sendReport(is, body.get(email)); // SES 发送 return ok; } catch (IOException | WriteException e) { e.printStackTrace(); } return error; }流程拉取活跃项 →WriteExcel.write生成.xls内存流 →SendMessages.sendReport以附件形式发到body.email。返回 JSON 字符串ok或错误信息。从源码结构看仓库版本将Gson gson new Gson()声明在try块内而catch之后的return gson.toJson(error)在 try 块外引用了它教程文档中的版本把Gson声明在try之外并返回error happened。复现时建议以文档版本为准把Gson的声明提到try之前避免变量作用域编译问题。5.6 WorkItem数据模型WorkItem.java 是纯 POJO字段与 Work 表一一对应public class WorkItem { private String id; private String name; private String guide; private String date; private String description; private String status; private boolean archived; // 各字段的 getter/setter略 }Spring 会把ListWorkItem序列化为 JSON 数组返回给前端例如id、name、date、guide、description、status、archived七个字段。5.7 WriteExceljxl 生成报表 SES 发送WriteExcel.java 由两部分组成。第一部分write(IterableWorkItem items)动态生成 Excel 工作簿使用 jxl 2.6.10 APIpublic InputStream write(IterableWorkItem items) throws IOException, WriteException { ByteArrayOutputStream os new ByteArrayOutputStream(); WorkbookSettings wbSettings new WorkbookSettings(); wbSettings.setLocale(new Locale(en, US)); WritableWorkbook workbook Workbook.createWorkbook(os, wbSettings); workbook.createSheet(Work Item Report, 0); WritableSheet excelSheet workbook.getSheet(0); addLabels(excelSheet); // 表头Writer / Date / Guide / Description / Status fillContent(excelSheet, items); // 数据从第 2 行row2开始逐行写入 workbook.write(); workbook.close(); return new ByteArrayInputStream(os.toByteArray()); }细节静态块中预定义了 Times 10pt 普通与“加粗下划线”两种WritableCellFormataddField对超过 200 字符的文本把列宽上限截到 150避免列宽失控fillContent的row从 2 开始即第 1 行是表头、第 2 行空行后的数据起始约定代码里 row2 即跳过一行。第二部分内部Component类SendMessages用 SDK v2 SES 发送带附件邮件Component public static class SendMessages { private static String sender Enter value; // 必须替换为已验证地址 private static String subject Weekly AWS Status Report; private static String bodyText Hello,\r\n\r\nPlease see the attached file for a weekly update.; private static String bodyHTML !DOCTYPE htmlhtml lang\en-US\bodyh1Hello!/h1.../body/html; private static String attachmentName WorkReport.xls; public void sendReport(InputStream is, String emailAddress) throws IOException { byte[] fileContent IOUtils.toByteArray(is); try { send(makeEmail(fileContent, emailAddress)); } catch (MessagingException e) { e.printStackTrace(); } } public void send(MimeMessage message) throws MessagingException, IOException { ByteArrayOutputStream outputStream new ByteArrayOutputStream(); message.writeTo(outputStream); ByteBuffer buf ByteBuffer.wrap(outputStream.toByteArray()); byte[] arr new byte[buf.remaining()]; buf.get(arr); SdkBytes data SdkBytes.fromByteArray(arr); RawMessage rawMessage RawMessage.builder().data(data).build(); SendRawEmailRequest rawEmailRequest SendRawEmailRequest.builder().rawMessage(rawMessage).build(); SesClient client SesClient.builder().region(Region.US_WEST_2).build(); client.sendRawEmail(rawEmailRequest); } }这条链路的设计值得拆解makeEmail用 Jakarta Mail API 组装一个mixed 多部分 MIME 邮件内层alternative同时放纯文本与 HTML 正文外层再挂一个附件部分MimeBodyPartDataHandler附件名WorkReport.xlsMimeMessage.writeTo(ByteArrayOutputStream)把整个 MIME 结构序列化为 RFC 822 原始报文原始报文经SdkBytes.fromByteArray包装进RawMessage调用 SES 的sendRawEmail——即 SDK 不做字段级拼装直接发送已构造好的 MIMESesClient同样硬编码Region.US_WEST_2须与 SES 已验证邮箱所在区域一致。必须修改sender为你在 SES 中已验证verified的发件邮箱否则邮件不会发出仓库源码中预填了作者的测试地址文档中以Enter value占位复现时一律替换为自己的验证地址。6. 运行 Spring REST API 与接口验证在 IntelliJ 中直接运行主类App。首次运行选择 main 类的运行图标即可。API 基地址为http://localhost:8080支持以下端点方法与路径说明GET /api/items返回Work表全部数据项不传参数时 flag-1GET /api/items?archivedtrue/?archivedfalse仅返回归档 / 仅返回活跃数据项PUT /api/items/{id}:archive将指定项转为归档项POST /api/items新增一项请求体含 name/guide/description/statusPOST api/items:report生成活跃项报表并以邮件发送浏览器访问http://localhost:8080/api/items可看到 JSON 数组响应即 images/json2.png 所示格式用于验证 REST API 在挂前端之前已可用。7. 接入 React 前端React 前端是一个使用 Cloudscape 组件库的 SPA教程原文指向官方的 “Work item tracker web client” 示例工程可自行下载对应 React 工程模板消费上述全部端点。唯一需要修改的配置是前端config.json中的BASE_URL确保它指向你的 Spring 应用{ BASE_URL: http://localhost:8080/api }前端行为与第 1 节的能力清单对应Active/Archived 两个 Tab 分别调用GET /api/items?archivedfalse|trueArchive 按钮调用PUT /api/items/{id}:archive新增表单调用POST /api/itemsEmail Report 输入框 Send report 按钮调用POST api/items:report其中email字段即收件人。8. 教程文档与仓库源码的差异备忘按文档步骤复刻时对照仓库实际源码会发现几处小差异复现前请先明确App 类文档版本定义了public static final Region region Region.US_EAST_1;常量仓库的 App.java 仅保留SpringBootApplication与main并未使用 Region 字段。ReportController如 5.5 节所述Gson变量作用域存在 try 内外不一致建议采用文档版本Gson声明在try之前。WriteExcel.SendMessages.sender仓库源码预填了作者邮箱文档使用占位符两者都强调必须替换为 SES 已验证地址。区域硬编码DatabaseService与SendMessages均硬编码Region.US_WEST_2且 Secrets Manager 使用ProfileCredentialsProvider。如果你的 RDS 实例、Secret 或 SES 邮箱在其他区域需要相应修改这两处 Region并保证本地 AWS 凭据有secretsmanager:GetSecretValue与ses:SendRawEmail权限。POM 坐标文档让 GroupId 填aws-spring仓库 pom.xml 实际为org.example不影响运行以能解析依赖为准。9. 收尾与清理完成联调后请删除本教程创建的全部资源RDS 实例、Secrets Manager 秘密等以避免持续计费。该案例展示了 aws-doc-sdk-examples 中典型的 javav2 多服务模式Spring Boot 负责 Web 与 JDBCSDK v2 负责 AWS 服务调用Secrets Manager 取凭据、SES 发附件邮件React SPA 通过 JSON 契约消费 REST API三者之间均以接口而非代码耦合可单独替换任一层例如把 React 换成其他前端或把 SES 换成其他通知渠道。更多同目录的多服务示例可参考 javav2/usecases 下的其他用例。赞分享示例工程教程后端【免费下载链接】aws-doc-sdk-examplesWelcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below.项目地址https://gitcode.com/gh_mirrors/aw/aws-doc-sdk-examples点击查看免费下载相关推荐基于 Spring Boot 与 Amazon Redshift 构建工作项跟踪 Web 应用aws-doc-sdk-examples/javav2基于 Spring Boot 与 Amazon Redshift 构建工作项跟踪 Web 应用aws doc sdk examples/javav2 本篇指示例工程教程后端Spring Boot 与 AWS SDK for Java v2构建 Amazon Aurora Serverless 工作项追踪 REST 应用Spring Boot 与 AWS SDK for Java v2构建 Amazon Aurora Serverless 工作项追踪 REST 应用 本篇以示例工程教程后端使用 AWS SDK for C 构建 Hello Amazon S3 入门项目基于 aws-doc-sdk-examples 的完整实战指南使用 AWS SDK for C 构建 Hello Amazon S3 入门项目基于 aws doc sdk examples 的完整实战指南 本指南围绕示例工程教程后端上一篇智能小说下载与格式转换工具提升阅读体验的全方位解决方案下一篇InternLM3-8B-Instruct 与 o1 推理范式在 self-llm 中复现类 o1 思维链的本地部署实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考