Java EE 企业应用 E03:有状态会话、单例与定时器各保存什么
两名采购员看见同一份待办,定时任务却跑了两遍 采购员 A 在浏览器填写草稿,审批员 B 正查看全租户积压的采购申请;后台每五分钟扫描超时的审批任务。把这三种状态全放进一个 @ApplicationScoped 普通对象,至少出现三个错误:A 的暂存字段可能被另一个请求改写,单例的计数器没有自然的持久性,定时线程在应用重启时也不会凭内存继续。Enterprise Beans 提供有状态会话对象、单例受管对象和 Timer Service 等不同契约,但它们各自解决的范围不一样。数据库中的采购申请、租户和审批结果仍是权威业务记录,不能用一个 Bean 实例替代。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 主线目前可核对的入口是 ProcurementUseCases 的 CDI 用例与 JdbcRequestStore 的显式 SQL。现有 server.xml 启用了 Jakarta EE 11 Platform,不能仅由特性配置推出 @Stateful、@Singleton 或持久...
Java EE 企业应用 E01:WebSocket 推送不是审批状态的账本
页面断线十秒,申请已经批准 申请人正看着一张 SUBMITTED 的采购单。审批员提交决定后,页面希望立刻显示 APPROVED;此时浏览器恰好断线。若客户端把最后收到的 WebSocket 消息当作状态账本,重新连上时永远停在旧状态。推送解决的是低延迟提示,不是状态持久性;权威值仍是服务器数据库中的申请状态、版本及受授权的查询。这个判断尤其关系到权限:一个能够连接到 /ws/requests/42 的用户,不自动有权查看 42 号申请。采购实验工程已有 ProcurementUseCases 与 JdbcRequestStore,没有认证 WebSocket 端点,也没有受保护的申请详情 GET;下述链路全部是选修设计,不能把 /api/health 的成功当作实时进度验收。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 先修不是可忽略的目录说明。06–11 需要提供明确的 Web 请求及 DTO,24–26 需要建立受认证主体、对象授权和可信租户上下文。眼下已有 /api/lab/...
Java EE 企业应用 E04:用一个本地 SOAP 系统划定遗留集成边界
采购订单提交后,遗留供应商系统没有响应 订单 726 已在本地 PostgreSQL 提交,适配器把确认请求发到遗留供应商 SOAP 服务。读响应时超时,供应商可能已经接收并创建单据,也可能没有收到任何字节。将这个异常直接解释为“外部操作失败”并立即用新请求号重试,供应商端可能创建第二份确认;把它当作成功,又可能永远缺少确认。本篇只选一个外部系统:受控本地 SOAP 1.2 供应商 stub,提供 RegisterOrder 操作、供应商幂等键及查询操作的合同。Connectors(Jakarta Connectors,JCA)资源适配器是另一种集成路径,不与本实验叠加,也不声称本地 SOAP stub 验证过资源适配器部署。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 目前累计工程的 ProcurementUseCases.order 在本地建订单;并无 SOAP stub、WSDL、外部映射表或 HTTP 出站适配器。deploy/server.xml 的受管 jdbc/Proc...
Java EE 企业应用 E06:同一采购用例在 Jakarta EE 与 Spring Boot/MyBatis 的职责对照
换了技术栈,审批结果不能跟着换 采购申请 519 属于租户 A,状态 SUBMITTED,版本 7,总额 4900 元;主体只有 A 的 APPROVER 角色且限额 5000 元。审批后预期状态 APPROVED、版本 8,只形成一次审计事件。另一张跨租户或超额的申请不应因从 Jakarta EE 改用 Spring Boot/MyBatis 而变得可批;若事务回滚,审批和审计必须一起消失。这个例子是技术栈对照的固定输入,而不是本机已执行的受保护采购接口。现有 WAR 暂未接入可信 principal、审批额度和审计表;先修 04、09、12–23、35 的实际实验范围必须如实核对,不用后续章节的题目代替已经完成的代码。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 在 Jakarta EE 版本,HTTP 入口由 Servlet/REST 承接,容器创建 CDI 或会话 bean,用受管 DataSource 执行 JdbcRequestStore 的参数化 SQL;受管事务决定提...
Java EE 企业应用 E07:`javax` 改名之前先锁定旧系统契约
WAR 在新容器报类找不到,订单 SQL 却完全没变 遗留采购应用由 Java EE 8 的 Servlet、REST、CDI 和事务 API 编译,打包后部署到使用 Jakarta EE 11 的运行时。一些源文件引用 javax.servlet.*,新容器的企业 API 在 jakarta.servlet.*;旧 WAR 即使有相同 URL 和 SQL,也不能据此推断二进制兼容。有人用全局字符串替换把 javax.sql.DataSource 改成 jakarta.sql.DataSource,编译立刻失败:JDBC 的 javax.sql 属于 Java SE,迁移企业 API 时不改包名。迁移验收应核对同一采购用例在旧/新环境的请求、事务、身份和失败契约,而非仅以编译结果定案。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 E07 必须从独立旧工程开始:冻结 Java EE 8 应用服务器具体发行版、JDK 发行版/补丁、Maven 与每项 API 依赖,保存原始 WAR 哈希...
Java EE 企业应用 E05:两项真实 XA 资源如何在崩溃后定案
采购库提交了,外部结算库还停在 prepare 采购订单在数据库 A,供应商结算记录在数据库 B。两个普通 JDBC 连接分别 commit(),即使都写在一个 Java 方法里,A 成功、B 抛错时也无法撤销 A。把业务代码包上 @Transactional,如果连接本身不是可登记到同一事务协调器的 XA 资源,仍不能声称两个资源原子提交。本篇实验必须使用两个真正不同的资源管理器,例如两个隔离 PostgreSQL 16 实例、各自 XA-capable DataSource,由同一个 Jakarta Transactions 协调器管理;主线只有一个 jdbc/Procurement 普通数据源,至今没有第二库、XA 配置或协调器恢复日志,故所有 XA 路径 NOT_RUN。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 XA 提供的是协调两个事务分支的协议,不保证每一步都不会失败。对每一订单,应用先在 A 写 purchase_order 或其财务提交标志,在 B 写 suppl...
Java EE 企业应用 E08:同一 WAR 在第二运行时能证明什么
原运行时 /api/health 正常,新运行时却找不到数据源 Open Liberty 的 server.xml 定义 jdbc/Procurement、PostgreSQL 驱动位置、主机端口、WAR 上下文根;迁移到第二个 Jakarta EE 运行时,把同一个 WAR 文件复制进去,不会自动复制服务器的资源定义。部署可能失败,也可能首次 getConnection() 才报错。即便健康端点返回 ready,它不查库、不验证认证或消息;不能把一张 HTTP 200 截图写成“采购审批系统已可移植”。E08 的先修是主线 00–39 的完整业务、权限、消息及交付验收;若先修不齐,应明确写未覆盖,而不是从早期 DataSource 探针外推全链等价。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 第二运行时已选 Eclipse GlassFish 8.0.4。writing-plans/javaee-enterprise/VERSIONS.md 记录该版的厂商 EE 11/JDK 2...
软件建模39:综合评审——怎样证明模型值得维护
一个模型通过当前预订场景,还不能说明它能够承受变化。延期会改变占用窗口,换设备会改变资源身份,追溯改价会改变历史金额的解释。三者都可能破坏“原请求重放仍返回原结果”这条看似独立的规则。 本篇对冻结的27-v1施加这三张变化卡,在旁边增加教学用的版本二状态层。原核心没有被修改,也没有把新功能倒写成既有应用的能力。每张卡都有实际成功路径、反例和保留事实的检查。 冻结基线再定义变化身份 初始快照有两台在役设备。原申请占用 E1 的十点到十一点,E1 另有十二点开始的租期,E2 则在十点半到十一点二十分被占用。所有时间采用 UTC,报价沿用每小时十二元五角,按小时向上取整。 27-v1规定请求号标识完整的原申请和费率,不能拿同一个号改租期或设备。因此修订需要新的预订请求号;变化操作还需要自己的 operationId,以识别一次延期、换设备或调价的重试。这两个身份解决不同层次的重复,不可互换。 版本二的 State 包含原应用快照、变化回执、金额调整记录和前后请求关系。收到相同操作号与相同负载时返回历史结果;同号不同负载直接拒绝。变化的原因和修订目标由显式命令传入,不从当前目录或现有活...
软件建模38:从领域模型到应用架构——哪些决定还没有作出
明确设备身份、半开租期和取消历史以后,应用仍然可以采用不同的代码组织与部署方式。领域模型没有决定请求从哪里进入、状态存在哪里、消息失败由谁处理,也没有决定需要几个进程。 本篇以冻结的27-v1为参照,执行事务脚本、领域实现和模块门面三种候选,再通过一次真实的本机 HTTP 请求贯穿入口、用例、仓储和发布。实验只在单个 JVM 中运行,逻辑边界与部署边界分别说明。 三个候选保持哪一段行为 共同轨迹包含首次确认、重叠拒绝、相邻确认、取消、晚时钟下重放旧确认、重放旧失败、未知设备、提前量不足,以及复用请求号却改变设备。比较对象包括每一步结果、最终活动数量、完整回执以及历史报价,不能只比较第一条请求返回的字符串。 事务脚本候选把判断集中在 reserve 中:读取设备数据,检查状态与租期,扫描当前占用,计算报价,保存活动记录和回执。它仍复用标识、租期与报价值规则,因此不是故意把所有基础类型退回字符串;主要差别是业务流程的判断顺序集中在一个过程里。 领域候选直接调用27的 RentalApplication,再由真实的设备、Specification、Factory 和 Reservat...
软件建模37:遗留系统建模——表和代码已经存在怎么办
遗留表中的 ready 字段为 Y,不足以证明设备能接受新的租赁。现有程序可能只检查 ready 与 busy,完全没有读取检验结果和退役标志。把表反向生成类图,会忠实展示这些字段,却不会指出旧判断遗漏了什么。 本篇从一份合成遗留 CSV 和真实执行的旧谓词出发,把设备逐批转换到冻结的27-v1应用。旧字段、未知编码和前后行为差异都保留在实验中。这里没有真实厂商数据,也没有已经获批的生产迁移方案。 从字段追到可观察行为 遗留记录包含 code、ready、busy、inspection、retired 和 note。旧查询的可租条件只有 ready=Y 且 busy=N,设备检验和退役状态不参与判断。四条样本都满足这个布尔条件,但分别表示检验通过、等待检验、已经退役以及未知检验码。 “旧程序这样计算”是一条可以运行验证的事实。“业务就应该这样决定”则是另一项判断。当前系列已经明确待检和退役设备不能新租,因此旧谓词在这两个样本上的放行属于需要改变的行为,不能以保持兼容为由永久保留。 教学输入约定 ready 表示维修侧已经放行,busy 只表示遗留系统记录了某种占用,inspec...









