大白话说Java设计模式-09-建造者模式(源码剖析篇):JDK / Spring / MyBatis 中的建造者实现
📌 一句话本质:建造者在源码里不是"教科书式"的 Director + Builder,而是"链式调用 + 自动校验"的简化版。
🏷️ 标签:建造者模式 / JDK StringBuilder / Spring UriComponentsBuilder / MyBatis 🎯 适合:中高级后端 / 想读懂主流框架源码的工程师
目录
- 一、为什么读建造者源码?一张地图先看清
- 二、JDK 源码剖析:3 个经典建造者实现
- 三、Spring / MyBatis 源码剖析:URL 与 SQL 建造者
- 四、为什么框架作者这么设计?
- 五、借鉴到我们项目:3 个"抄作业"实践
- 六、本篇小结 + 下一模式预告
一、为什么读建造者源码?一张地图先看清
建造者模式在源码里出现的频率比抽象工厂高得多——因为"复杂对象分步构造"是个普遍需求。
源码地图:
| JDK | java.lang | StringBuilder | String |
| JDK | java.lang | StringBuffer | String(线程安全) |
| JDK | java.util.stream | Stream.Builder | Stream |
| JDK | java.nio | ByteBuffer | 字节缓冲区 |
| JDK | java.util | Locale.Builder | Locale |
| Spring | Spring Web | UriComponentsBuilder | URI |
| Spring | Spring Web | HttpHeaders | HTTP 头 |
| MyBatis | MyBatis | SqlSessionFactoryBuilder | SqlSessionFactory |
| Lombok | Lombok | @Builder 注解 | 任意类 |
| Guava | Guava | ImmutableList.builder() | ImmutableList |
我们按"JDK → Spring/MyBatis"的顺序来剖析。
二、JDK 源码剖析:3 个经典建造者实现
2.1 StringBuilder —— 字符串拼接的"链式建造者"
源码位置:
- OpenJDK 17
- src/java.base/share/classes/java/lang/StringBuilder.java
核心字段:
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, Comparable<StringBuilder>, CharSequence {
/**
* 默认容量
*/
static final int DEFAULT_CAPACITY = 16;
}
核心方法(简化版):
/**
* ✅ 链式 append:每一步都返回 this
*/
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
@Override
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
/**
* ✅ 工厂方法:build 出最终 String
*/
@Override
public String toString() {
// 创建新 String
return new String(value, 0, count);
}
为什么 StringBuilder 是"建造者"?
完整流程:
StringBuilder builder = new StringBuilder();
↓ append("Hello")
builder = "Hello"
↓ append(" ")
builder = "Hello "
↓ append("World")
builder = "Hello World"
↓ toString()
result = "Hello World" (新 String 对象)
为什么 StringBuilder 是"链式"而不是"分步赋值"?
因为字符串构造是高频操作,需要极致的简洁。链式调用比"分步 + build()"快 50%。
2.2 Stream.Builder —— 流式 API 的"建造者"
源码位置:
- OpenJDK 17
- src/java.base/share/classes/java/util/stream/Stream.java
核心源码:
public interface Stream<T> extends BaseStream<T, Stream<T>> {
/**
* ✅ 内部 Builder 接口
*/
interface Builder<T> extends Consumer<T> {
@Override
Builder<T> accept(T t); // 添加元素
default Builder<T> add(T t) {
accept(t);
return this;
}
Stream<T> build(); // ✅ 工厂方法:build 出 Stream
}
/**
* ✅ 工厂方法:获取 Builder
*/
static <T> Builder<T> builder() {
return new Streams.StreamBuilderImpl<>();
}
}
完整使用:
/**
* ✅ 链式构建 Stream
*/
Stream<String> stream = Stream.<String>builder()
.add("Hello")
.add("World")
.add("!")
.build();
stream.forEach(System.out::println);
关键点:
| add(T t) | 链式添加元素(返回 this) |
| accept(T t) | Consumer 接口实现(返回 this) |
| build() | 不可再变的 Stream |
为什么 Stream 用 Builder?
2.3 ByteBuffer —— NIO 的"字节缓冲区建造者"
源码位置:
- OpenJDK 17
- src/java.base/share/classes/java/nio/ByteBuffer.java
核心源码(简化版):
public abstract class ByteBuffer extends Buffer
implements Comparable<ByteBuffer> {
// … 字段 …
/**
* ✅ 链式 put:每一步都返回 this
*/
public ByteBuffer put(byte b) {
// 实际逻辑在 HeapByteBuffer 中
return this;
}
public ByteBuffer put(int index, byte b) {
// …
return this;
}
/**
* ✅ 链式 put(多字节)
*/
public ByteBuffer put(byte[] src, int offset, int length) {
// …
return this;
}
/**
* ✅ 链式 flip:切换读写模式
*/
public ByteBuffer flip() {
super.flip();
return this;
}
}
完整使用:
/**
* ✅ 链式构造 ByteBuffer
*/
ByteBuffer buffer = ByteBuffer.allocate(1024)
.put((byte) 1)
.put((byte) 2)
.put((byte) 3)
.flip(); // 切到读模式
while (buffer.hasRemaining()) {
System.out.println(buffer.get());
}
关键点:
| allocate(int) | 工厂方法分配内存 |
| put(…) | 链式写入数据 |
| flip() | 切换读写模式 |
| get() | 读数据 |
为什么 ByteBuffer 是"建造者"?
2.4 JDK 建造者的"作者选择"
| StringBuilder | 10+ | 高频操作,极致简洁 |
| Stream.Builder | 3 | 流式 API,不可变结果 |
| ByteBuffer | 20+ | 分阶段状态管理 |
| Locale.Builder | 10 | 复杂配置对象 |
规律:
JDK 的建造者倾向于"链式 + 极简"——没有 Director,没有显式 build(),方法名直接就是操作名。这是 JDK 的"轻量级建造者"风格。
三、Spring / MyBatis 源码剖析:URL 与 SQL 建造者
3.1 Spring UriComponentsBuilder —— URL 拼装的"标准建造者"
源码位置:
- Spring Framework 6.1.x
- spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java
核心源码(简化版):
public class UriComponentsBuilder implements UriBuilder, Cloneable {
private String scheme;
private String ssp;
private String userInfo;
private String host;
private String port;
private CompositePathComponentBuilder pathBuilder;
private MultiValueMap<String, String> queryParams;
private String fragment;
/**
* ✅ 工厂方法:创建 Builder
*/
public static UriComponentsBuilder newInstance() {
return new UriComponentsBuilder();
}
public static UriComponentsBuilder fromPath(String path) {
return new UriComponentsBuilder().path(path);
}
public static UriComponentsBuilder fromUri(String uri) {
return new UriComponentsBuilder().uri(uri);
}
/**
* ✅ 链式设置 scheme
*/
public UriComponentsBuilder scheme(String scheme) {
this.scheme = scheme;
return this;
}
/**
* ✅ 链式设置 host
*/
public UriComponentsBuilder host(String host) {
this.host = host;
return this;
}
/**
* ✅ 链式设置 port
*/
public UriComponentsBuilder port(int port) {
this.port = String.valueOf(port);
return this;
}
/**
* ✅ 链式设置 path
*/
public UriComponentsBuilder path(String path) {
this.pathBuilder.addPath(path);
return this;
}
/**
* ✅ 链式添加 query 参数
*/
public UriComponentsBuilder queryParam(String name, Object... values) {
this.queryParams.add(name, Arrays.stream(values).map(Object::toString).toArray(String[]::new));
return this;
}
/**
* ✅ 链式设置 fragment
*/
public UriComponentsBuilder fragment(String fragment) {
this.fragment = fragment;
return this;
}
/**
* ✅ 工厂方法:build 出 UriComponents
*/
public UriComponents build() {
return buildInternal(false);
}
public UriComponents build(boolean encoded) {
return buildInternal(encoded);
}
/**
* ✅ 模板方法:build 内部逻辑
*/
private UriComponents buildInternal(boolean encoded) {
// 1. 组合 path
// 2. 处理 query
// 3. 处理 fragment
// 4. 返回 UriComponents
return new UriComponents(scheme, ssp, userInfo, host, port,
pathBuilder.build(), queryParams, fragment, encoded);
}
}
完整使用:
/**
* ✅ 链式拼装 URL
*/
URI uri = UriComponentsBuilder.newInstance()
.scheme("https")
.host("api.dabai.com")
.path("/orders/{id}")
.queryParam("source", "PC")
.queryParam("userId", "{userId}")
.build()
.expand("ORDER_001", 1001L)
.toUri();
// 最终 URL:https://api.dabai.com/orders/ORDER_001?source=PC&userId=1001
关键点:
| newInstance() / fromPath() | 工厂方法获取 Builder |
| scheme() / host() / port() | 链式设置各个部分 |
| path() | 链式添加路径(支持占位符) |
| queryParam() | 链式添加 query 参数 |
| build() | build 出 UriComponents |
| expand() | 替换占位符 |
为什么 UriComponentsBuilder 是"建造者"?
3.2 Spring HttpHeaders —— HTTP 头的"链式封装"
源码位置:
- Spring Framework 6.1.x
- spring-web/src/main/java/org/springframework/http/HttpHeaders.java
核心源码(简化版):
public class HttpHeaders implements MultiValueMap<String, String> {
private final Map<String, List<String>> headers;
/**
* ✅ 工厂方法:创建空的 HttpHeaders
*/
public static HttpHeaders empty() {
return new HttpHeaders(CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>()));
}
public static HttpHeaders writableHttpHeaders(HttpHeaders headers) {
return new WritableHttpHeaders(headers);
}
/**
* ✅ 链式添加单值头
*/
public HttpHeaders add(String headerName, String headerValue) {
this.headers.putIfAbsent(headerName, new ArrayList<>());
this.headers.get(headerName).add(headerValue);
return this;
}
/**
* ✅ 链式设置 Content-Type
*/
public HttpHeaders contentType(MediaType mediaType) {
return set(CONTENT_TYPE, mediaType.toString());
}
/**
* ✅ 链式设置 Authorization
*/
public HttpHeaders setBearerAuth(String token) {
return set(AUTHORIZATION, "Bearer " + token);
}
// … 大量链式 setter
}
完整使用:
/**
* ✅ 链式构造 HTTP 头
*/
HttpHeaders headers = HttpHeaders.empty()
.contentType(MediaType.APPLICATION_JSON)
.setBearerAuth("xxx-xxx-xxx")
.add("X-Request-Id", "REQ_001")
.add("X-User-Id", "1001");
关键点:
| 链式调用 | 所有 setter 返回 this |
| 类型安全 | 提供常用 header 的强类型方法(setBearerAuth) |
| 大小写不敏感 | 用 LinkedCaseInsensitiveMap |
3.3 MyBatis SqlSessionFactoryBuilder —— 复杂 SQL 会话工厂的"重型建造者"
源码位置:
- MyBatis 3.5.x
- src/main/java/org/apache/ibatis/session/SqlSessionFactoryBuilder.java
核心源码:
public class SqlSessionFactoryBuilder {
/**
* ✅ 工厂方法入口
*/
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// 1️⃣ 解析 XML(用 XMLConfigBuilder 解析)
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 2️⃣ 解析得到 Configuration
Configuration config = parser.parse();
// 3️⃣ 工厂方法:build 出 SqlSessionFactory
return build(config);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
}
}
/**
* ✅ 工厂方法:build 出 SqlSessionFactory
*/
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
注意:MyBatis 的 SqlSessionFactoryBuilder 不是典型的"链式建造者",而是"工厂方法"风格的建造者——只有 1 个核心 build() 方法,不返回 this。
MyBatis 用法:
/**
* 经典用法
*/
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// ✅ 链式构造 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
为什么 MyBatis 的 Builder 是"极简"风格?
SqlSessionFactoryBuilder 的产品族:
SqlSessionFactoryBuilder
↓ build(Reader)
XMLConfigBuilder
↓ parse()
Configuration
↓ build()
DefaultSqlSessionFactory
为什么这里不是"经典 4 角色"建造者?
因为 MyBatis 的"复杂对象"是 SqlSessionFactory,所有配置在 XML 里。Builder 只是"读取 XML + 创建对象"的桥梁。不是分步构造,而是"一次解析 + 一次创建"。
3.4 中间件建造者对比表
| JDK | StringBuilder | String | 链式 |
| JDK | Stream.Builder | Stream | 链式 |
| JDK | ByteBuffer | 字节缓冲区 | 链式 |
| Spring | UriComponentsBuilder | UriComponents | 链式 |
| Spring | HttpHeaders | HTTP 头 | 链式 |
| MyBatis | SqlSessionFactoryBuilder | SqlSessionFactory | 工厂方法(不链式) |
规律:
JDK / Spring 倾向"链式",MyBatis 倾向"工厂方法"。原因:JDK/Spring 的建造者是运行时高频操作(URL 拼装、字符串拼接),需要链式简洁;MyBatis 是启动期低频操作,工厂方法足够。
四、为什么框架作者这么设计?
读源码看的是"实现",悟的是"设计哲学"。
4.1 框架作者选建造者的 4 个核心考虑
| ① | 对象字段多 | URL 6 个、订单 30+ |
| ② | 构造过程分步 | 字符串拼接、URL 拼装 |
| ③ | 需要不可变结果 | String、Stream、UriComponents |
| ④ | 高频操作需要简洁 | 字符串拼接 |
4.2 链式 vs 分步:源码里怎么选?
| 运行时高频(字符串、URL) | 链式 | 极简,无中间变量 |
| 构造期低频(启动期) | 分步 | 可读性 > 简洁性 |
| 需要 Director | 分步 | 固定流程,可封装 |
| 没有固定流程 | 链式 | 灵活拼接 |
4.3 框架作者不选建造者的 3 种情况
| ① | 对象字段少于 3 个 | 简单 DTO(用构造方法) |
| ② | 构造过程不可分 | 解析 XML(用工厂方法) |
| ③ | 需要可变对象 | JavaBean(用 setter) |
4.4 建造者 vs 工厂方法:源码里怎么选?
| 复杂对象分步构造 | 建造者 | URL、字符串、订单 |
| 按类型创建对象 | 工厂方法 | 支付渠道、日志框架 |
| 配套 API | 抽象工厂 | JDBC Connection、Document |
五、借鉴到我们项目:3 个"抄作业"实践
5.1 实践 1:SQL 查询建造者
场景:动态 SQL 拼装(条件查询)。
完整实现:
/**
* 借鉴 UriComponentsBuilder:动态 SQL 查询
*/
public class SqlQueryBuilder {
private StringBuilder select = new StringBuilder("SELECT *");
private StringBuilder from = new StringBuilder();
private StringBuilder where = new StringBuilder();
private StringBuilder orderBy = new StringBuilder();
private StringBuilder limit = new StringBuilder();
private List<Object> params = new ArrayList<>();
/**
* ✅ 链式:设置 SELECT
*/
public SqlQueryBuilder select(String... columns) {
this.select = new StringBuilder("SELECT " + String.join(", ", columns));
return this;
}
/**
* ✅ 链式:设置 FROM
*/
public SqlQueryBuilder from(String table) {
this.from = new StringBuilder(" FROM " + table);
return this;
}
/**
* ✅ 链式:添加 WHERE 条件
*/
public SqlQueryBuilder where(String condition, Object... values) {
if (this.where.length() == 0) {
this.where.append(" WHERE ");
} else {
this.where.append(" AND ");
}
this.where.append(condition);
this.params.addAll(Arrays.asList(values));
return this;
}
/**
* ✅ 链式:设置 ORDER BY
*/
public SqlQueryBuilder orderBy(String column, boolean desc) {
this.orderBy.append(" ORDER BY ").append(column);
if (desc) this.orderBy.append(" DESC");
return this;
}
/**
* ✅ 链式:设置 LIMIT
*/
public SqlQueryBuilder limit(int limit, int offset) {
this.limit.append(" LIMIT ").append(limit).append(" OFFSET ").append(offset);
return this;
}
/**
* ✅ 工厂方法:build 出 SQL
*/
public QueryResult build() {
String sql = select.toString()
+ from.toString()
+ where.toString()
+ orderBy.toString()
+ limit.toString();
return new QueryResult(sql, params);
}
/**
* 查询结果:SQL + 参数
*/
public record QueryResult(String sql, List<Object> params) {}
}
使用示例:
/**
* ✅ 链式构造 SQL
*/
QueryResult query = new SqlQueryBuilder()
.select("id", "name", "price")
.from("orders")
.where("user_id = ?", 1001L)
.where("status = ?", "PAID")
.orderBy("create_time", true)
.limit(20, 0)
.build();
// sql = "SELECT id, name, price FROM orders WHERE user_id = ? AND status = ? ORDER BY create_time DESC LIMIT 20 OFFSET 0"
// params = [1001L, "PAID"]
收益:
| 链式调用 | 条件动态拼装,可读性高 |
| 统一参数收集 | 不再到处拼接 ? |
| 类型安全 | 编译期检查参数类型 |
5.2 实践 2:HTTP 客户端请求建造者
场景:调用第三方 API 时的请求拼装。
完整实现:
/**
* 借鉴 UriComponentsBuilder:HTTP 请求建造者
*/
public class HttpRequestBuilder {
private String method = "GET";
private String url;
private HttpHeaders headers = new HttpHeaders();
private String body;
private Duration timeout = Duration.ofSeconds(10);
/**
* ✅ 链式:设置方法
*/
public HttpRequestBuilder method(String method) {
this.method = method;
return this;
}
public HttpRequestBuilder get() {
return method("GET");
}
public HttpRequestBuilder post() {
return method("POST");
}
public HttpRequestBuilder put() {
return method("PUT");
}
public HttpRequestBuilder delete() {
return method("DELETE");
}
/**
* ✅ 链式:设置 URL
*/
public HttpRequestBuilder url(String url) {
this.url = url;
return this;
}
/**
* ✅ 链式:设置 URL(带占位符)
*/
public HttpRequestBuilder url(String urlTemplate, Object... uriVars) {
URI uri = UriComponentsBuilder.fromUriString(urlTemplate)
.buildAndExpand(uriVars)
.encode()
.toUri();
this.url = uri.toString();
return this;
}
/**
* ✅ 链式:添加 Header
*/
public HttpRequestBuilder header(String name, String value) {
this.headers.add(name, value);
return this;
}
/**
* ✅ 链式:设置 JSON Body
*/
public HttpRequestBuilder jsonBody(Object body) {
this.headers.setContentType(MediaType.APPLICATION_JSON);
try {
this.body = new ObjectMapper().writeValueAsString(body);
} catch (JsonProcessingException e) {
throw new RuntimeException("序列化请求体失败", e);
}
return this;
}
/**
* ✅ 链式:设置超时
*/
public HttpRequestBuilder timeout(Duration timeout) {
this.timeout = timeout;
return this;
}
/**
* ✅ 工厂方法:build 出 HTTP 请求
*/
public HttpRequest build() {
if (url == null) {
throw new IllegalArgumentException("URL 不能为空");
}
return new HttpRequest(method, url, headers, body, timeout);
}
/**
* HTTP 请求对象(不可变)
*/
public record HttpRequest(
String method,
String url,
HttpHeaders headers,
String body,
Duration timeout
) {}
}
使用示例:
/**
* ✅ 链式构造 HTTP 请求
*/
HttpRequest request = new HttpRequestBuilder()
.post()
.url("https://api.dabai.com/orders/{id}", "ORDER_001")
.header("Authorization", "Bearer xxx")
.header("X-Request-Id", "REQ_001")
.jsonBody(orderRequest)
.timeout(Duration.ofSeconds(5))
.build();
收益:
| 链式调用 | 请求拼装清晰 |
| URL 占位符 | 自动 expand |
| 不可变结果 | 线程安全 |
| 统一超时 | 避免每个调用都设置 |
5.3 实践 3:分页参数建造者
场景:所有分页查询统一分页参数。
完整实现:
/**
* 借鉴 UriComponentsBuilder:分页参数建造者
*/
public class PageQueryBuilder {
private int pageNum = 1;
private int pageSize = 20;
private String orderBy;
private boolean asc = true;
private final Map<String, Object> filters = new HashMap<>();
/**
* ✅ 链式:设置页码
*/
public PageQueryBuilder pageNum(int pageNum) {
if (pageNum < 1) {
throw new IllegalArgumentException("页码不能小于 1");
}
this.pageNum = pageNum;
return this;
}
/**
* ✅ 链式:设置每页大小
*/
public PageQueryBuilder pageSize(int pageSize) {
if (pageSize < 1 || pageSize > 1000) {
throw new IllegalArgumentException("每页大小必须在 1~1000 之间");
}
this.pageSize = pageSize;
return this;
}
/**
* ✅ 链式:设置排序
*/
public PageQueryBuilder orderBy(String orderBy) {
this.orderBy = orderBy;
return this;
}
public PageQueryBuilder asc() {
this.asc = true;
return this;
}
public PageQueryBuilder desc() {
this.asc = false;
return this;
}
/**
* ✅ 链式:添加过滤条件
*/
public PageQueryBuilder filter(String key, Object value) {
if (value != null) {
this.filters.put(key, value);
}
return this;
}
/**
* ✅ 工厂方法:build 出分页参数
*/
public PageQuery build() {
int offset = (pageNum – 1) * pageSize;
return new PageQuery(pageNum, pageSize, offset, orderBy, asc, filters);
}
/**
* 分页参数(不可变)
*/
public record PageQuery(
int pageNum,
int pageSize,
int offset,
String orderBy,
boolean asc,
Map<String, Object> filters
) {}
}
使用示例:
/**
* ✅ 链式构造分页参数
*/
PageQuery query = new PageQueryBuilder()
.pageNum(1)
.pageSize(20)
.orderBy("create_time")
.desc()
.filter("status", "PAID")
.filter("user_id", 1001L)
.build();
收益:
| 链式调用 | 分页参数清晰 |
| 自动 offset 计算 | 不再算 offset = (page-1) * size |
| 默认值合理 | pageNum=1, pageSize=20 |
| 校验集中 | 在 builder 里校验 |
六、本篇小结 + 下一模式预告
6.1 本篇小结(5 个核心要点)
6.2 一句话总结
建造者在源码里不是"教科书式"的 Director + Builder,而是"链式调用 + 自动校验"的简化版。JDK 用链式做高频操作,Spring 用链式拼 URL/Header,MyBatis 用工厂方法做启动期配置。选 Builder 还是 Factory,看操作频率。
6.3 知识脑图
建造者源码剖析
├── JDK
│ ├── StringBuilder(链式 append)
│ ├── Stream.Builder(链式 add + build)
│ └── ByteBuffer(链式 put + flip)
├── Spring
│ ├── UriComponentsBuilder(URL 拼装)
│ └── HttpHeaders(HTTP 头链式封装)
├── MyBatis
│ └── SqlSessionFactoryBuilder(工厂方法式)
└── 借鉴价值
├── SQL 查询建造者(动态 SQL)
├── HTTP 请求建造者(请求拼装)
└── 分页参数建造者(分页统一)
6.4 下一模式预告
第 10 篇【原型模式 – 业务实战篇】:大白商城商品 SKU 克隆的"复制魔法"
下一篇我们会实战原型模式,回答 4 个问题:
并附完整的商品克隆代码(含浅克隆 / 深克隆 / MapStruct 三种实现 + 单元测试),可直接复制到 IDEA 跑。
觉得对您有帮助,麻烦点点关注啦,您的关注是我创作的最大动力~ 🎯
网硕互联帮助中心



评论前必须登录!
注册