模板方法模式适合封装http请求通用生命周期,其中buildrequest必须为abstract(因url、method、body等无默认值),handleresponse负责状态码与重试逻辑,parseresponsebody专注反序列化且返回业务对象,logrequest/logresponse设为可选钩子以适配日志策略。

模板方法模式适合封装 HTTP 请求的通用生命周期,比如统一日志、重试、错误转换、响应解析等;真正需要子类介入的,只是请求构造、参数组装、业务响应映射这三处。关键不是“能不能用”,而是“哪些步骤必须 abstract,哪些该用 hook 控制”。
什么时候该把 buildRequest 设为抽象方法而不是钩子
因为每个子类发的请求目标(URL、method、body 结构)几乎必然不同,且没有合理默认值。如果设成钩子并提供空实现,容易导致子类忘记重写而静默失败;强制 abstract 能在编译期暴露问题。
-
buildRequest必须是abstract:URL、HTTP method、请求头、body 序列化逻辑都高度定制,无共用默认行为 - 避免用
protected void buildRequest() { }这种空钩子——它不会报错,但运行时request为 null,后续 NPE 难定位 - 若某些子类只需改 header(如加 token),可额外提供
addCustomHeaders钩子,而非动buildRequest
handleResponse 和 parseResponseBody 的职责怎么切分
前者负责状态码判断、全局错误码拦截、重试决策;后者只做 JSON 反序列化或字段提取。混在一起会导致子类既要处理业务逻辑又要操心网络层细节。
-
handleResponse是模板方法中调用的 protected 方法,返回boolean表示是否成功(决定是否继续执行后续步骤) -
parseResponseBody是 abstract 方法,输入String或InputStream,输出具体业务对象(如OrderDTO) - 不要让子类在
parseResponseBody里 throwRuntimeException——异常应由模板方法统一捕获并转为ApiException
为什么 logRequest 和 logResponse 要设计成钩子而非具体方法
因为日志粒度和敏感信息脱敏策略因项目而异:有的要打印完整 body,有的只打 URL 和耗时,有的需过滤 password 字段。用钩子能避免父类强耦合日志框架或脱敏逻辑。
- 默认实现为空:
protected void logRequest(HttpRequest req) { },子类按需重写 - 若重写时用了
slf4j的logger.debug,注意 MDC 上下文是否已注入 traceId —— 模板方法无法保证,得靠子类自己处理 - 别在钩子里做耗时操作(如写磁盘日志),否则拖慢整个请求链路
Spring Boot 中如何避免 @Autowired 在抽象父类中失效
抽象类本身不会被 Spring 扫描为 Bean,所以不能直接在父类里 @Autowired service 或 rest template。正确做法是把依赖通过构造函数传入子类,再由子类透传给父类模板。
- 父类构造函数接收
RestTemplate或WebClient实例,存为final字段 - 子类用
@Autowired注入依赖后,在构造函数中调用super(restTemplate) - 不要用
@Resource或@Autowired注入到抽象父类字段——字段永远为 null,运行时报 NPE
最容易被忽略的是钩子方法的执行时机:比如 beforeExecute 钩子若用于修改请求体,就必须在 buildRequest 之后、实际发送之前调用;顺序错乱会导致子类逻辑白写。模板方法的流程定义比代码本身更关键。










