<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>《Java 编程实战》 · 全书目录与章节索引（55 篇） on PlumePHP</title><link>https://plumephp.com/posts/books/java%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98/</link><description>Recent content in 《Java 编程实战》 · 全书目录与章节索引（55 篇） on PlumePHP</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Sun, 25 Oct 2026 14:00:00 +0800</lastBuildDate><atom:link href="https://plumephp.com/posts/books/java%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98/index.xml" rel="self" type="application/rss+xml"/><item><title>《Java 编程实战》附录 D 与 Spring Boot 卷的衔接</title><link>https://plumephp.com/java-practice-appendix-d-spring-boot-bridge/</link><pubDate>Sun, 25 Oct 2026 14:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-appendix-d-spring-boot-bridge/</guid><description>把 libraryd 的手工装配逐项映射到 Spring Boot 的对应能力：模块与依赖、数据访问、事务、Web 路由、参数校验、错误契约、日志、指标、缓存、测试与打包，并给出从本书代码迁移到 Spring Boot 的顺序与最容易踩错的五个差异点。</description></item><item><title>《Java 编程实战》附录 C 故障排查</title><link>https://plumephp.com/java-practice-appendix-c-troubleshooting/</link><pubDate>Sun, 25 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-appendix-c-troubleshooting/</guid><description>按「构建、数据库、迁移、连接池、事务、HTTP、容器」七个层次组织 libraryd 的常见故障：给出真实报错原文、触发条件、定位命令与修复方式，并说明排查时为什么应先确认失败发生在哪一层，而不是先改配置。附录末尾附一页式诊断命令清单，便于按层逐项核对。</description></item><item><title>《Java 编程实战》附录 B 依赖选型</title><link>https://plumephp.com/java-practice-appendix-b-dependency-selection/</link><pubDate>Sun, 25 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-appendix-b-dependency-selection/</guid><description>把 libraryd 用到的 JDBC 驱动、连接池、迁移工具、HTTP 框架、JSON、校验、日志、指标、追踪、缓存与测试库放在一起对比，说明每个选择的约束、替代方案与不适用场景，并给出一张可复用的选型表、四个常见误判与替换前的检查清单。</description></item><item><title>《Java 编程实战》附录 A Maven 配置模板</title><link>https://plumephp.com/java-practice-appendix-a-maven-config/</link><pubDate>Sun, 25 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-appendix-a-maven-config/</guid><description>给出一套可直接对照的 libraryd 多模块 Maven 配置：父 POM 的属性与 dependencyManagement、五个子模块的依赖声明、编译与测试插件、Enforcer、JaCoCo、Checkstyle、SpotBugs、Shade、Jib 的完整片段，以及 quality、integration 两个 profile 与常用诊断命令。</description></item><item><title>《Java 编程实战》目录</title><link>https://plumephp.com/java-practice-toc/</link><pubDate>Sat, 24 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-toc/</guid><description>《Java 编程实战》全书导航页：说明从卷一 CLI 到 libraryd 服务的升级路线、读者画像与前置知识，给出六周实战路径与四类读者的入口建议，并按六个部分列出 16 章 48 节的完整索引，另附序、前言、附录 A–D，每节配一句话定位与项目状态说明。</description></item><item><title>《Java 编程实战》前言</title><link>https://plumephp.com/java-practice-preface/</link><pubDate>Sat, 24 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-preface/</guid><description>交代本书的读者对象、前置知识与六部分结构，列出 JDK 25、Maven、Docker 与 PostgreSQL 17 的环境清单和真实版本输出，说明代码、输出、容器与命令的书写约定，以及三种阅读路径、验证代码的四个层次与版本不一致时的排查顺序。</description></item><item><title>《Java 编程实战》序</title><link>https://plumephp.com/java-practice-foreword/</link><pubDate>Sat, 24 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-foreword/</guid><description>说明卷二为什么在卷一之后单独成书、为什么不用 Spring 而是手工搭出 libraryd 的每个基础设施位置，交代从 CLI 到可部署服务的六道工程门槛、与 Spring Boot 卷和本站 Java 专题的分工，以及本书刻意不写的 JVM 机制、GC 调优与验证方式。</description></item><item><title>《Java 编程实战》16.3 发布与回滚：配置、迁移与灰度</title><link>https://plumephp.com/java-practice-16-3-release-rollback/</link><pubDate>Fri, 23 Oct 2026 16:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-16-3-release-rollback/</guid><description>镜像能启动不等于能安全发布。本节用 libraryd 的真实启动失败、Flyway checksum 冲突和 /health 503 输出，讲清环境变量配置、只前进的迁移、扩展-收缩改造，以及灰度发布、回滚判据和连接池容量的具体计算方法等。</description></item><item><title>《Java 编程实战》16.2 容器化：多阶段 Dockerfile 与 Jib</title><link>https://plumephp.com/java-practice-16-2-docker-jib/</link><pubDate>Fri, 23 Oct 2026 15:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-16-2-docker-jib/</guid><description>fat jar 解决了依赖问题，但目标机器仍然需要 JRE、正确的用户和健康检查。本节用多阶段 Dockerfile 把 libraryd 装进非 root 镜像，再用 Jib 3.5.2 在不依赖 Docker daemon 的情况下构建，并给出 352 MB 镜像的真实运行结果。</description></item><item><title>《Java 编程实战》16.1 打包：fat jar、jlink 与运行时镜像</title><link>https://plumephp.com/java-practice-16-1-packaging-runtime/</link><pubDate>Fri, 23 Oct 2026 14:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-16-1-packaging-runtime/</guid><description>mvn package 默认只产出几十 KB 的瘦 jar，离开 Maven 就找不到依赖。本节用 Shade 3.6.2 生成可执行的 libraryd fat jar，处理 SPI 与签名文件，再用 jdeps 和 jlink 裁出 53 MB 运行时，并给出真实的 jar 大小与启动耗时。</description></item><item><title>《Java 编程实战》15.3 端到端与 CI：把服务跑起来验证</title><link>https://plumephp.com/java-practice-15-3-e2e-ci/</link><pubDate>Fri, 23 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-15-3-e2e-ci/</guid><description>单元、持久化和 Web 测试都绿，并不代表 libraryd 能启动。本节用随机端口启动完整 LibraryApplication，走一遍借阅、续借、归还、健康检查和指标，并把 mvn verify 接进 GitHub Actions，给出 33 个测试的真实耗时。</description></item><item><title>《Java 编程实战》15.2 Testcontainers：真实 PostgreSQL 集成测试</title><link>https://plumephp.com/java-practice-15-2-testcontainers-postgres/</link><pubDate>Fri, 23 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-15-2-testcontainers-postgres/</guid><description>手工 docker run 的数据库会随开发机和 CI 漂移，H2 又撑不起 libraryd 的部分索引和 pg_trgm。本节用 Testcontainers 1.21.4 启动 postgres:17-alpine，讲清静态容器、连接注入、Flyway 迁移、容器复用与清理的真实行为。</description></item><item><title>《Java 编程实战》15.1 测试金字塔：单元、集成与端到端</title><link>https://plumephp.com/java-practice-15-1-test-pyramid/</link><pubDate>Fri, 23 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-15-1-test-pyramid/</guid><description>libraryd 有 33 个测试，但把领域、仓储、HTTP 和端到端混在一起跑，会让失败定位和反馈速度一起变差。本节按模块划出四层测试，说明每层该依赖什么、不该依赖什么，并用真实的测试数量、耗时与配比解释为什么端到端只保留两个主链路用例。</description></item><item><title>《Java 编程实战》14.3 分布式缓存：Redis 与一致性取舍</title><link>https://plumephp.com/java-practice-14-3-distributed-cache/</link><pubDate>Thu, 22 Oct 2026 16:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-14-3-distributed-cache/</guid><description>两个 libraryd 实例，A 更新了图书，B 却还在返回旧标题。本节从真实的两实例输出出发，讲清 Redis 的 key、TTL 与序列化约定，Jedis 的 300ms 超时和故障降级，写路径的失效顺序，以及 Pub/Sub、短 TTL、版本号三种一致性方案的取舍。</description></item><item><title>《Java 编程实战》14.2 缓存模式：Cache-Aside、穿透与雪崩</title><link>https://plumephp.com/java-practice-14-2-cache-patterns/</link><pubDate>Thu, 22 Oct 2026 15:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-14-2-cache-patterns/</guid><description>查不到的 ISBN 为什么每次都能打到数据库？热点 key 过期的一瞬间，八个线程会不会同时回源？本节用 libraryd 的真实输出拆解缓存穿透、击穿与雪崩，给出负缓存、Caffeine 单飞、TTL 抖动和 Cache-Aside 写路径的取舍，并指出多实例下仍会漏掉的并发窗口。</description></item><item><title>《Java 编程实战》14.1 本地缓存：Caffeine 与失效策略</title><link>https://plumephp.com/java-practice-14-1-local-cache/</link><pubDate>Thu, 22 Oct 2026 14:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-14-1-local-cache/</guid><description>同一本图书在一分钟内被查询几百次，每次都打 PostgreSQL 既慢又浪费。本节用 Caffeine 3.3.1 给 libraryd 加本地缓存，讲清 maximumSize、expireAfterWrite、refreshAfterWrite 与 recordStats 的取舍，并用真实输出解释 estimatedSize 为什么暂时偏大、刷新为什么先返回旧值。</description></item><item><title>《Java 编程实战》13.3 健康检查：依赖探测与就绪/存活</title><link>https://plumephp.com/java-practice-13-3-health-checks/</link><pubDate>Thu, 22 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-13-3-health-checks/</guid><description>数据库挂掉时 /health 该返回 200 还是 503？本节从 libraryd 真实的 5 秒探测延迟出发，讲清存活、就绪与启动探针的边界，拆解 HealthProbe 的 connection.isValid(2) 到底限制了什么，Hikari 的 connectionTimeout 如何主导探针耗时，并给出 Kubernetes 探针参数与拆分方案。</description></item><item><title>《Java 编程实战》13.2 追踪：OpenTelemetry 与 W3C traceparent</title><link>https://plumephp.com/java-practice-13-2-tracing/</link><pubDate>Thu, 22 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-13-2-tracing/</guid><description>当一次借阅请求跨过网关、libraryd 和数据库，只有 requestId 的日志就拼不出完整链路。本节讲清 trace、span 与 W3C traceparent 的字段含义，拆解 libraryd 的 Telemetry 与 TracingMiddleware 如何提取父上下文、创建 SERVER span、回写响应头，并用真实输出讨论采样、子 span 与常见错误。</description></item><item><title>《Java 编程实战》13.1 Micrometer：计数器、计时器与 Prometheus</title><link>https://plumephp.com/java-practice-13-1-micrometer-metrics/</link><pubDate>Thu, 22 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-13-1-micrometer-metrics/</guid><description>日志告诉你发生了什么，指标告诉你发生得有多频繁。本节用 Micrometer 1.17.2 与 Prometheus registry 给 libraryd 接上计数器、计时器和仪表，讲清命名、tag 基数、SimpleMeterRegistry 测试写法，以及 /metrics 的真实抓取输出。</description></item><item><title>《Java 编程实战》12.3 日志规范：脱敏、采样与成本</title><link>https://plumephp.com/java-practice-12-3-log-hygiene/</link><pubDate>Wed, 21 Oct 2026 16:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-12-3-log-hygiene/</guid><description>日志里能放什么、该留多久、量有多大，是三个独立问题。本节用 libraryd 的真实输出演示邮箱与令牌脱敏、CRLF 日志注入、按比例采样与 Logback TurboFilter，并算清不同请求量下的日志体积，最后给出一份可执行的级别与保留策略。</description></item><item><title>《Java 编程实战》12.2 结构化日志：JSON、MDC 与请求上下文</title><link>https://plumephp.com/java-practice-12-2-structured-logging/</link><pubDate>Wed, 21 Oct 2026 15:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-12-2-structured-logging/</guid><description>文本日志无法按 requestId 聚合一次借阅的多个步骤。本节用 logstash-logback-encoder 9.0 把 libraryd 的日志输出成 JSON，讲清 MDC 的线程绑定与泄漏、requestId 与 traceId 的生命周期、StructuredArguments 字段，以及线程池之间的上下文传递。</description></item><item><title>《Java 编程实战》12.1 SLF4J 与 Logback：级别、Appender 与配置</title><link>https://plumephp.com/java-practice-12-1-slf4j-logback/</link><pubDate>Wed, 21 Oct 2026 14:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-12-1-slf4j-logback/</guid><description>libraryd 的日志一度混着 Javalin 自带的 slf4j-simple，格式和级别都不受控。本节用 SLF4J 2.0.20 与 Logback 1.6.6 的真实输出讲清门面与绑定的关系、级别继承、Appender 与 encoder、参数化日志和异常打印，最后落到 libraryd 的两份配置文件。</description></item><item><title>《Java 编程实战》11.3 任务幂等：重复执行不出错</title><link>https://plumephp.com/java-practice-11-3-job-idempotency/</link><pubDate>Wed, 21 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-11-3-job-idempotency/</guid><description>定时任务可能因为重启、重试或多实例被跑两次。本节先用 job_runs 的唯一键和 on conflict do nothing 抢占执行权，再用真实输出说明第二次抢占返回 0；接着展示一个真实踩坑：用 fine_cents = 0 当未结算标记，第二次运行又处理了 1546 行零罚金记录，最后用 fine_settled_at 修正。</description></item><item><title>《Java 编程实战》11.2 批处理：游标、分片与罚金结算</title><link>https://plumephp.com/java-practice-11-2-batch-processing/</link><pubDate>Wed, 21 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-11-2-batch-processing/</guid><description>6000 行借阅记录，逐条插入约 11–21 秒，500 条一批约 0.17–0.44 秒；5400 行罚金结算同样差距明显。本节在同一台机器上跑出这组数字，再讲清楚 PostgreSQL 游标、keyset 分页、每批提交与按 id 分片，最后把 FineSettlementJob 写成可重入、可观测的批处理。</description></item><item><title>《Java 编程实战》11.1 ScheduledExecutorService：周期任务与时钟</title><link>https://plumephp.com/java-practice-11-1-scheduled-tasks/</link><pubDate>Wed, 21 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-11-1-scheduled-tasks/</guid><description>libraryd 需要每天扫逾期、每月结算罚金。本节用 ScheduledExecutorService 的 fixedRate 与 fixedDelay 实跑对比：200ms 任务配 100ms 周期时，fixedRate 每约 200ms 才启动一次，fixedDelay 每约 300ms 一次；再说明周期任务抛异常会静默停止，以及如何用注入的 Clock 算出下一次整点执行时间。</description></item><item><title>《Java 编程实战》10.3 异步边界：阻塞、上下文与可观测性</title><link>https://plumephp.com/java-practice-10-3-async-boundaries/</link><pubDate>Tue, 20 Oct 2026 16:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-10-3-async-boundaries/</guid><description>异步任务换线程之后，MDC 里的 requestId 会变成空，ThreadLocal 也读不到；InheritableThreadLocal 只在创建线程时复制一次，线程池复用后照样丢。本节用真实日志展示这三类丢失，给出 MdcExecutor 传递与清理方案，并讨论阻塞 JDBC、orTimeout 不取消上游、异常解包这几个边界。</description></item><item><title>《Java 编程实战》10.2 异步编排：并行查询与结果合并</title><link>https://plumephp.com/java-practice-10-2-async-composition/</link><pubDate>Tue, 20 Oct 2026 15:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-10-2-async-composition/</guid><description>一次会员借阅详情要查借阅、会员和书目，串行是 600ms，并行是 210ms，但并行不等于无限扇出。本节用 thenCompose 表达两波依赖，用 allOf 合并独立查询，并把每本书一次查询的 N+1 改成一次 findByIsbns 批量查询，最后用信号量把数据库并发压回连接池能承受的范围。</description></item><item><title>《Java 编程实战》10.1 CompletableFuture：组合、异常与超时</title><link>https://plumephp.com/java-practice-10-1-completable-future/</link><pubDate>Tue, 20 Oct 2026 14:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-10-1-completable-future/</guid><description>CompletableFuture 不是把 Future 换个名字：thenApply、thenCompose、thenCombine 决定依赖关系，orTimeout 与 exceptionally 决定失败路径。本节用 500ms 任务配 200ms 超时、allOf 单任务失败等输出，说明超时不会取消上游、allOf 会等所有任务结束，再把并行查询接进 libraryd 搜索。</description></item><item><title>《Java 编程实战》9.3 背压与拒绝策略：让过载可见</title><link>https://plumephp.com/java-practice-09-3-backpressure-rejection/</link><pubDate>Tue, 20 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-09-3-backpressure-rejection/</guid><description>线程池满了之后，AbortPolicy 只会抛一个 RejectedExecutionException，调用方看到的却是 500。本节把队列深度、活跃线程数和拒绝计数变成可观测信号，用自定义拒绝处理器把过载映射成 503，并讨论 CallerRunsPolicy 为什么会把压力传回 Jetty 请求线程。</description></item><item><title>《Java 编程实战》9.2 虚拟线程：在 libraryd 里替换平台线程</title><link>https://plumephp.com/java-practice-09-2-virtual-threads/</link><pubDate>Tue, 20 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-09-2-virtual-threads/</guid><description>JDK 25 的虚拟线程早已不是预览特性。本节先在同一个 JVM 参数下对比 2000 个平台线程与 2000/100000 个虚拟线程的创建耗时和 RSS，再用 Javalin 7.2.4 的 concurrency.useVirtualThreads 打开虚拟线程，最后用 JFR 确认 synchronized 在 JDK 25 里不再 pin，并说明结构化并发为何仍不进入本卷。</description></item><item><title>《Java 编程实战》9.1 ExecutorService：线程池参数与队列</title><link>https://plumephp.com/java-practice-09-1-executor-service/</link><pubDate>Tue, 20 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-09-1-executor-service/</guid><description>libraryd 的阻塞查询不能直接跑在 Jetty 请求线程上。本节用 ThreadPoolExecutor 把核心线程数、最大线程数、有界队列和拒绝策略讲清楚，并用 2 个线程、3 个队列位的饱和实验展示第 5 个任务之后会发生什么，最后把线程池接进 AppMain 与优雅停机。</description></item><item><title>《Java 编程实战》8.3 异常映射：从领域错误到 HTTP 响应</title><link>https://plumephp.com/java-practice-08-3-exception-mapping/</link><pubDate>Mon, 19 Oct 2026 16:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-08-3-exception-mapping/</guid><description>领域层抛 LibraryException，持久层抛 SQLException，web 层只认识 Problem Details。本节把三层翻译串起来：SQLState 怎么变成错误码，错误码怎么决定 HTTP 状态，异常处理器为什么按最具体类型匹配，以及 404、405 和处理器自己抛异常时会发生什么。</description></item><item><title>《Java 编程实战》8.2 错误契约：RFC 9457 Problem Details</title><link>https://plumephp.com/java-practice-08-2-problem-details/</link><pubDate>Mon, 19 Oct 2026 15:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-08-2-problem-details/</guid><description>同样是 400，有的接口返回字符串，有的返回对象，客户端只能按状态码猜。本节按 RFC 9457 把 libraryd 的错误响应固定成 application/problem+json，讲清 type、title、status、detail 与扩展成员 code、requestId、errors，并用真实响应说明媒体类型被覆盖的坑。</description></item><item><title>《Java 编程实战》8.1 参数校验：Jakarta Validation 与手写规则</title><link>https://plumephp.com/java-practice-08-1-validation-rules/</link><pubDate>Mon, 19 Oct 2026 14:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-08-1-validation-rules/</guid><description>libraryd 的 bodyAsClass 只负责解析，不负责判断字段是否为空、超长或越界。本节用 Jakarta Validation 3.1.1 与 Hibernate Validator 9.1.5 把约束写在 record 上，把 ConstraintViolation 收成字段错误 Map，并用真实输出说明 Expressly 缺失、null 与空白、消息语言和领域校验的分工。</description></item><item><title>《Java 编程实战》7.3 优雅停机与连接管理</title><link>https://plumephp.com/java-practice-07-3-graceful-shutdown/</link><pubDate>Mon, 19 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-07-3-graceful-shutdown/</guid><description>进程收到 SIGTERM 时，libraryd 正在处理的借阅请求会不会被切断，连接池和定时任务会不会来不及收尾。本节用两次真实停机实验对比 stopTimeout=0 与 10 秒的差别，再给出 LibraryApplication 的关闭顺序、幂等关闭、启动失败回滚和 shutdown hook 的完整写法。</description></item><item><title>《Java 编程实战》7.2 中间件：请求 ID、访问日志与错误处理</title><link>https://plumephp.com/java-practice-07-2-middleware-errors/</link><pubDate>Mon, 19 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-07-2-middleware-errors/</guid><description>libraryd 的每个 handler 都在重复读请求头、拼日志、记耗时。本节用 Javalin 的 before/after 把请求 ID、MDC、访问日志和指标收进中间件，并用真实执行顺序说明异常处理器与 after 的先后关系，最后给出请求头信任、MDC 清理与指标基数的边界。</description></item><item><title>《Java 编程实战》7.1 Javalin 起步：路由、路径参数与 JSON</title><link>https://plumephp.com/java-practice-07-1-javalin-routing/</link><pubDate>Mon, 19 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-07-1-javalin-routing/</guid><description>libraryd 的服务层已经能借书还书，却还没有 HTTP 入口。本节用 Javalin 7.2.4 把路由挂在配置对象上，接通路径参数、查询参数、分页默认值与 Jackson，并用真实请求验证 201、204、Location、日期序列化和内容类型检查，最后给出完整路由清单。</description></item><item><title>《Java 编程实战》6.3 序列化边界：多态、循环引用与 JSONB</title><link>https://plumephp.com/java-practice-06-3-serialization-boundaries/</link><pubDate>Sun, 18 Oct 2026 16:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-06-3-serialization-boundaries/</guid><description>审计事件要按类型还原，领域对象互相引用又会让 Jackson 无限递归。本节真实复现多态类型丢失、嵌套深度 1002 的报错，并演示如何把事件安全写入 PostgreSQL 的 jsonb 列、再用宽松 mapper 读回，最后给出 libraryd 的 AuditEvent 与仓储写法。</description></item><item><title>《Java 编程实战》6.2 DTO 设计：请求、响应与领域对象的边界</title><link>https://plumephp.com/java-practice-06-2-dto-design/</link><pubDate>Sun, 18 Oct 2026 15:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-06-2-dto-design/</guid><description>领域 record 不是 API 契约：Loan 没有 status 和 overdueDays，请求直接绑定领域对象又会带来批量赋值。本节用 Jackson 真实输出设计 BookDto、LoanDto、PageResponse 与 ApiError，划清请求、响应和领域对象的边界。</description></item><item><title>《Java 编程实战》6.1 Jackson 基础：绑定、命名与日期</title><link>https://plumephp.com/java-practice-06-1-jackson-basics/</link><pubDate>Sun, 18 Oct 2026 14:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-06-1-jackson-basics/</guid><description>HTTP 层不能靠字符串拼接生成 JSON。本节用 Jackson 2.22.4 真实演示 record 绑定、命名策略、JavaTimeModule 与 ISO-8601 日期，以及 FAIL_ON_UNKNOWN_PROPERTIES 的报错原文，最后把配置收进 libraryd 的单例 JsonSupport。</description></item><item><title>《Java 编程实战》5.3 并发控制：唯一索引、条件更新与幂等</title><link>https://plumephp.com/java-practice-05-3-concurrency-control/</link><pubDate>Sun, 18 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-05-3-concurrency-control/</guid><description>两个借阅请求同时通过「没有活动借阅」检查时，应用层的先查再插并不安全。本节用两个 JDBC 连接真实复现部分唯一索引冲突、条件更新幂等归还与死锁 40P01，并给出 libraryd 借阅、归还和批量写的最终写法，说明为什么 synchronized 在多实例下无效。</description></item><item><title>《Java 编程实战》5.2 隔离级别与 MVCC：PostgreSQL 的真实行为</title><link>https://plumephp.com/java-practice-05-2-isolation-mvcc/</link><pubDate>Sun, 18 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-05-2-isolation-mvcc/</guid><description>两个连接同时读写 loans 时，read committed 与 repeatable read 到底差在哪？本节用真实 PostgreSQL 输出拆开语句快照与事务快照，观察 xmin/xmax 与行锁等待，并给出 libraryd 报表查询与可重试事务的落地写法。</description></item><item><title>《Java 编程实战》5.1 JDBC 事务：提交、回滚与事务边界</title><link>https://plumephp.com/java-practice-05-1-transactions-jdbc/</link><pubDate>Sun, 18 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-05-1-transactions-jdbc/</guid><description>libraryd 的一次借阅要写会员和借阅两条 SQL，JDBC 默认的自动提交会让失败只回滚一半。本节用真实 PostgreSQL 演示 autoCommit=false、commit、rollback 与保存点，把事务边界收在服务方法上，并解释 25P02 与连接归还问题。</description></item><item><title>《Java 编程实战》4.3 分页、批量写与 N+1</title><link>https://plumephp.com/java-practice-04-3-pagination-batch/</link><pubDate>Sat, 17 Oct 2026 16:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-04-3-pagination-batch/</guid><description>列表页、批量导入和借阅详情会把数据访问层的问题放大。本节实现 LIMIT/OFFSET 分页与总数统计，比较逐条插入、addBatch 和 reWriteBatchedInserts 的真实耗时，并用一次 JOIN 消灭活动借阅列表里的 N+1 查询。</description></item><item><title>《Java 编程实战》4.2 索引与执行计划：读懂 EXPLAIN</title><link>https://plumephp.com/java-practice-04-2-indexes-explain/</link><pubDate>Sat, 17 Oct 2026 15:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-04-2-indexes-explain/</guid><description>小数据量下所有查询都很快，索引问题要等到十万行才暴露。本节用 EXPLAIN (ANALYZE, BUFFERS) 对比 Seq Scan、Index Scan 与 Bitmap Index Scan，给 libraryd 的模糊搜索和活动借阅补上 pg_trgm GIN 索引与部分索引。</description></item><item><title>《Java 编程实战》4.1 表设计：约束、范式与 libraryd 的 schema</title><link>https://plumephp.com/java-practice-04-1-schema-design/</link><pubDate>Sat, 17 Oct 2026 14:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-04-1-schema-design/</guid><description>数据库 schema 是 libraryd 的最终契约，也是应用层之外的最后一道校验。本节逐条解释 V1__init.sql 里的三张表、主外键、检查约束和部分唯一索引，并用真实插入失败验证每条规则挡住的是什么错误，以及哪些规则不该交给应用层，哪些又必须留给数据库。</description></item><item><title>《Java 编程实战》3.3 数据访问层：把 SQL 装进 Repository</title><link>https://plumephp.com/java-practice-03-3-repository-jdbc/</link><pubDate>Sat, 17 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-03-3-repository-jdbc/</guid><description>SQL 散落在服务层会让事务、错误码和测试都变得难以收口。本节把图书、会员、借阅三类 SQL 收进 JdbcBookRepository、JdbcMemberRepository 与 JdbcLoanRepository，用行映射还原 record，并把 SQLState 翻译成 libraryd 的 ErrorCode。</description></item><item><title>《Java 编程实战》3.2 HikariCP：连接池参数与泄漏排查</title><link>https://plumephp.com/java-practice-03-2-connection-pool/</link><pubDate>Sat, 17 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-03-2-connection-pool/</guid><description>每条请求都新建物理连接会拖慢 libraryd，也很快耗尽 PostgreSQL 的连接数。本节用 HikariCP 建立有界连接池，讲清 maximumPoolSize、connectionTimeout、maxLifetime 与 leakDetectionThreshold 的含义，并用真实日志定位连接泄漏和池耗尽。</description></item><item><title>《Java 编程实战》3.1 JDBC 基础：Connection、PreparedStatement 与结果集</title><link>https://plumephp.com/java-practice-03-1-jdbc-basics/</link><pubDate>Sat, 17 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-03-1-jdbc-basics/</guid><description>libraryd 从内存仓储走向 PostgreSQL，第一步是把 JDBC 的四个对象用对：DriverManager 与 DataSource 的取舍、PreparedStatement 的参数绑定、ResultSet 的列读取，以及生成键与资源关闭。本节用真实容器跑通图书和借阅记录的写入与查询。</description></item><item><title>《Java 编程实战》2.3 质量门禁：JaCoCo 覆盖率与 CI 流水线</title><link>https://plumephp.com/java-practice-02-3-quality-gate/</link><pubDate>Fri, 16 Oct 2026 16:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-02-3-quality-gate/</guid><description>把测试从「跑过」变成「可验收」：用 JaCoCo 的 LINE 与 BRANCH 计数读懂报告，按模块设置 60% 行覆盖率门槛，在 quality profile 里串起编译、测试、Checkstyle、SpotBugs 和覆盖率，最后落到一条 CI 命令。</description></item><item><title>《Java 编程实战》2.2 静态分析：Checkstyle 与 SpotBugs 的规则取舍</title><link>https://plumephp.com/java-practice-02-2-static-analysis/</link><pubDate>Fri, 16 Oct 2026 15:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-02-2-static-analysis/</guid><description>区分 Checkstyle 管源码风格、SpotBugs 管字节码缺陷：给出一份只保留命名、导入、大括号和魔法数字的精简规则集，用真实违规输出解释阈值与抑制范围，再用 CT_CONSTRUCTOR_THROW 说明 SpotBugs 的第一条告警为什么把异常类声明为 final 就能修复。</description></item><item><title>《Java 编程实战》2.1 编译告警：把 -Xlint 变成门禁</title><link>https://plumephp.com/java-practice-02-1-compiler-warnings/</link><pubDate>Fri, 16 Oct 2026 14:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-02-1-compiler-warnings/</guid><description>把 javac 的 -Xlint 从编辑器提示变成构建门禁：先看 rawtypes 与 unchecked 的真实告警，再用 maven-compiler-plugin 的 -Xlint:all,-processing 加 -Werror，最后给出 libraryd 里处理泛型与受检异常的写法。</description></item><item><title>《Java 编程实战》1.3 可复现构建：插件锁定与可重复打包</title><link>https://plumephp.com/java-practice-01-3-build-reproducibility/</link><pubDate>Fri, 16 Oct 2026 11:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-01-3-build-reproducibility/</guid><description>让同一 commit 在任何机器上产出相同字节：锁定 Maven 生命周期插件版本，设置 project.build.outputTimestamp，连续两次 clean package 比较 jar 的 SHA-256，并说明 SNAPSHOT、时区与本地缓存会怎样破坏可复现性。</description></item><item><title>《Java 编程实战》1.2 依赖治理：BOM、作用域与版本冲突</title><link>https://plumephp.com/java-practice-01-2-dependency-governance/</link><pubDate>Fri, 16 Oct 2026 10:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-01-2-dependency-governance/</guid><description>把散落在子模块里的版本号收回父 POM：用 dependencyManagement 与 Jackson BOM 统一版本，用 runtime、test 作用域收紧依赖，再用 Enforcer 的 dependencyConvergence 抓出 Javalin 与 Jedis 带来的真实冲突并收敛。</description></item><item><title>《Java 编程实战》1.1 从单模块到多模块：libraryd 的拆分边界</title><link>https://plumephp.com/java-practice-01-1-multi-module-split/</link><pubDate>Fri, 16 Oct 2026 09:00:00 +0800</pubDate><guid>https://plumephp.com/java-practice-01-1-multi-module-split/</guid><description>把卷一的单模块 library 拆成 domain、persistence、web、app、integration-tests 五个模块：先按变化频率和依赖方向划边界，再写出可编译的父 POM、子模块 POM 与最小骨架，最后用 Maven reactor 和依赖树验证拆分结果。</description></item></channel></rss>