固定流程为什么适合交给框架
数据库访问有一套反复出现的步骤:获取连接、创建语句、执行、映射结果、释放资源。每个业务方法都手写一遍,容易漏掉关闭连接和异常处理。
Spring 的模板类把不变步骤收起来,把变化部分留成回调:
java
List<Order> orders = jdbcTemplate.query(
"select id, total from orders where user_id = ?",
(resultSet, row) -> new Order(
resultSet.getLong("id"),
resultSet.getLong("total")
),
userId
);业务代码提供 SQL、参数和映射逻辑;连接管理、异常转换和资源释放由模板负责。
模板方法的结构
把一次查询抽象成伪代码,控制流会更清楚:
java
<T> T execute(Callback<T> callback) {
Connection connection = dataSource.getConnection();
try {
return callback.run(connection);
} catch (SQLException error) {
throw translate(error);
} finally {
connection.close();
}
}框架控制主流程,Callback 是扩展点。这个思想也出现在任务执行器、HTTP 客户端、事务模板和测试生命周期中。
正在绘制图表…
查看 Mermaid 源码
flowchart LR
app[业务代码] --> template[Spring 模板]
template --> open[准备资源]
open --> callback[调用回调]
callback --> cleanup[统一清理]
cleanup --> app事件把不必要的直接依赖拆开
创建订单后,积分、通知和统计不一定要成为 OrderService 的同步依赖。可以发布一个领域事件:
java
record OrderCreated(String orderId) {}
@Service
class OrderService {
private final ApplicationEventPublisher events;
void createOrder(Order order) {
save(order);
events.publishEvent(new OrderCreated(order.id()));
}
}
@Component
class PointsListener {
@EventListener
void onOrderCreated(OrderCreated event) {
points.addFor(event.orderId());
}
}发布者只依赖事件发布器,不需要知道有多少个监听者。监听者可以独立增加或移除,但事件处理是否需要事务、是否允许异步,仍然要明确配置。
扩展点不是“随便插代码”
一个好的扩展点有清晰的输入、输出和执行时机:
- 模板负责哪些固定步骤;
- 回调可以改变哪些局部行为;
- 异常会被谁转换或传播;
- 资源由谁创建、谁释放。
如果这些边界说不清,扩展点就会变成隐藏控制流。读 Spring 源码时,可以先找模板类的主方法,再找它调用的回调接口和后置处理器。
这套思想如何迁移到自己的代码
当多个模块重复同一条可靠流程时,可以先画出不变步骤,再把业务差异收敛为接口或回调:
text
固定流程:校验 → 执行 → 记录 → 清理
变化部分:校验规则、执行动作、记录格式不要为了复用而强行抽象。只有当流程稳定、变化点重复出现,模板和扩展点才会减少复杂度。
下一篇会解释 Spring Boot 的自动配置:当这些容器、模板和扩展点越来越多时,框架如何根据条件替我们完成默认组装。