大白话说Java设计模式-07-抽象工厂模式(源码剖析篇):JDK / Spring / MyBatis 中的抽象工厂实现
📌 一句话本质:抽象工厂在源码里不是"教科书式"的族,而是"按规范凑齐一组相关 API"。
🏷️ 标签:抽象工厂模式 / JDK XML 解析 / JDBC / MyBatis 🎯 适合:中高级后端 / 想读懂主流框架源码的工程师
目录
- 一、为什么读抽象工厂源码?一张地图先看清
- 二、JDK 源码剖析:3 个抽象工厂经典实现
- 三、Spring / MyBatis 源码剖析:SQL 会话工厂家族
- 四、为什么框架作者这么设计?
- 五、借鉴到我们项目:3 个"抄作业"实践
- 六、本篇小结 + 下一模式预告
一、为什么读抽象工厂源码?一张地图先看清
抽象工厂在源码里出现的频率比工厂方法低——因为"产品族"这个概念本身就更严格。只有真正需要"配套"的地方才会用。
源码地图:
| JDK | JAXP XML | DocumentBuilderFactory | DocumentBuilder / Document |
| JDK | JAXP XSLT | TransformerFactory | Transformer / Templates |
| JDK | SAX | SAXParserFactory | SAXParser / XMLReader |
| JDK | JDBC | Connection | Statement / PreparedStatement / ResultSet |
| JDK | 文本 | NumberFormat 子类 | 货币/百分号/小数格式器 |
| Spring | Spring Data JPA | EntityManagerFactory | EntityManager / Query |
| MyBatis | MyBatis | SqlSessionFactory | SqlSession / Executor / StatementHandler |
| Hibernate | Hibernate | SessionFactory | Session / Query / Transaction |
我们按"JDK → Spring/MyBatis"的顺序来剖析。
二、JDK 源码剖析:3 个抽象工厂经典实现
2.1 JAXP DocumentBuilderFactory —— XML 解析的抽象工厂
源码位置:
- OpenJDK 17
- src/java.xml/share/classes/javax/xml/parsers/DocumentBuilderFactory.java
核心源码:
public abstract class DocumentBuilderFactory {
/**
* ✅ 抽象工厂方法:创建 DocumentBuilder
* <p>
* 注意:抽象工厂不一定有多个"create"方法。
* 这个抽象工厂只造一个核心产品(DocumentBuilder),
* 由 DocumentBuilder 内部再产生 Document 等其他产品。
*/
public abstract DocumentBuilder newDocumentBuilder() throws ParserConfigurationException;
/**
* 设置是否校验 XML
*/
public void setValidating(boolean validating) { ... }
/**
* 设置是否命名空间感知
*/
public void setNamespaceAware(boolean awareness) { ... }
}
完整的产品族结构:
抽象工厂:DocumentBuilderFactory
↓ newDocumentBuilder()
具体工厂实现:
├── com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl (默认)
└── … 第三方实现
具体产品:DocumentBuilder
↓ newDocument() / parse()
↓
最终产品:Document / Element / NodeList
为什么是抽象工厂?
虽然 DocumentBuilderFactory 只造一个产品(DocumentBuilder),但 DocumentBuilder 内部紧密耦合了一组相关 API(Document / Element / Attr / Text),这一组产品必须配套使用。换底层实现(Xerces / Crimson)时,整套 API 一起换。
大白商城的"抄作业":
/**
* 借鉴 DocumentBuilderFactory:自定义 XML 解析抽象工厂
*/
public abstract class XmlParserFactory {
public abstract XmlParser createParser();
public abstract void setValidating(boolean validating);
}
public class DefaultXmlParserFactory extends XmlParserFactory {
@Override
public XmlParser createParser() {
return new DefaultXmlParser();
}
}
2.2 JDBC Connection 家族 —— 数据库访问的抽象工厂
源码位置:
- OpenJDK 17
- src/java.sql/share/classes/java/sql/Connection.java
JDBC 抽象工厂的"产品族":
/**
* 抽象工厂:Connection
* <p>
* 一个 Connection 内部管理着一组相关产品
*/
public interface Connection extends Wrapper, AutoCloseable {
// ✅ 抽象工厂方法族
/** 创建 Statement(产品 1) */
Statement createStatement() throws SQLException;
/** 创建 PreparedStatement(产品 2) */
PreparedStatement prepareStatement(String sql) throws SQLException;
/** 创建 CallableStatement(产品 3) */
CallableStatement prepareCall(String sql) throws SQLException;
/** 创建 DatabaseMetaData(产品 4:元数据) */
DatabaseMetaData getMetaData() throws SQLException;
}
完整的产品族:
抽象工厂:Connection
↓
├── createStatement() → Statement
├── prepareStatement(sql) → PreparedStatement
├── prepareCall(sql) → CallableStatement
└── getMetaData() → DatabaseMetaData
具体工厂实现:
├── com.mysql.cj.jdbc.ConnectionImpl (MySQL)
├── oracle.jdbc.OracleConnection (Oracle)
└── org.postgresql.jdbc.PgConnection (PostgreSQL)
Statement 内部又是一个"小型抽象工厂":
public interface Statement extends Wrapper, AutoCloseable {
/** 执行 SQL,返回 ResultSet(产品 1) */
ResultSet executeQuery(String sql) throws SQLException;
/** 执行更新,返回影响行数 */
int executeUpdate(String sql) throws SQLException;
/** 批量执行(产品 2) */
void addBatch(String sql) throws SQLException;
}
JDBC 抽象工厂的"嵌套层级":
DriverManager.getConnection() // 第 1 层抽象工厂
↓
Connection // 第 2 层抽象工厂
↓
Statement / PreparedStatement // 第 3 层抽象工厂
↓
ResultSet // 最终产品
为什么是"嵌套"抽象工厂?
大白商城的"抄作业":
/**
* 借鉴 JDBC Connection:会话抽象工厂
* <p>
* 一个会话管理着一组相关操作
*/
public interface Session extends AutoCloseable {
/** 创建查询 */
Query createQuery(String sql);
/** 创建事务 */
Transaction createTransaction();
/** 获取元数据 */
Metadata getMetadata();
@Override
void close();
}
2.3 JAXP TransformerFactory —— XSLT 转换的抽象工厂
源码位置:
- OpenJDK 17
- src/java.xml/share/classes/javax/xml/transform/TransformerFactory.java
核心源码:
public abstract class TransformerFactory {
/**
* ✅ 抽象工厂方法
*/
public abstract Transformer newTransformer(Source source) throws TransformerConfigurationException;
public abstract Transformer newTransformer() throws TransformerConfigurationException;
/**
* 创建 Templates(预编译模板)
*/
public abstract Templates newTemplates(Source source) throws TransformerConfigurationException;
}
完整的产品族:
抽象工厂:TransformerFactory
↓ newTransformer() / newTemplates()
具体工厂实现:
├── com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
└── net.sf.saxon.TransformerFactoryImpl (Saxon)
产品族:
├── Transformer (执行转换)
├── Templates (预编译模板,可重用)
├── Source (输入源:DOMSource / SAXSource / StreamSource)
└── Result (输出目标:DOMResult / SAXResult / StreamResult)
为什么用抽象工厂?
XSLT 转换涉及一组配套 API:
- 输入:Source(多种实现)
- 输出:Result(多种实现)
- 转换器:Transformer / Templates
换底层实现(Xalan / Saxon)时,整套 API 一起换。
2.4 JDK 抽象工厂的"作者选择"
| DocumentBuilderFactory | 1 个 | 主要产品 + 内部 API 配套 |
| Connection | 4 个 | 嵌套生命周期,配套关系强 |
| TransformerFactory | 3 个 | 转换 + 模板 + 输入输出 |
规律:
JDK 的抽象工厂倾向于"窄接口"——抽象工厂方法少(1~4 个),但产品族内部紧密耦合。这种设计符合"接口隔离原则",调用方按需使用。
三、Spring / MyBatis 源码剖析:SQL 会话工厂家族
3.1 MyBatis SqlSessionFactory —— MyBatis 核心抽象工厂
源码位置:
- MyBatis 3.5.x
- src/main/java/org/apache/ibatis/session/SqlSessionFactory.java
核心源码:
public interface SqlSessionFactory {
/**
* ✅ 抽象工厂方法:打开 SqlSession
* <p>
* 每次调用都返回一个新的 SqlSession 实例
*/
SqlSession openSession();
/**
* 带自动提交参数
*/
SqlSession openSession(boolean autoCommit);
/**
* 带连接参数
*/
SqlSession openSession(Connection connection);
/**
* 带事务隔离级别
*/
SqlSession openSession(TransactionIsolationLevel level);
/**
* 带执行器类型
*/
SqlSession openSession(ExecutorType execType);
/**
* 获取 Configuration
*/
Configuration getConfiguration();
}
完整的产品族:
抽象工厂:SqlSessionFactory
↓ openSession()
具体工厂实现:
└── DefaultSqlSessionFactory
产品族(SqlSession 内部):
├── SqlSession (会话)
├── Executor (执行器:SimpleExecutor / ReuseExecutor / BatchExecutor)
├── StatementHandler (语句处理器)
├── ParameterHandler (参数处理器)
└── ResultSetHandler (结果集处理器)
SqlSession 内部结构:
public interface SqlSession extends Closeable {
// ✅ SqlSession 也是抽象工厂(嵌套)
/** 执行查询 */
<T> T selectOne(String statement);
/** 执行查询(带参数) */
<T> T selectOne(String statement, Object parameter);
/** 获取 Mapper(产品:动态代理的 Mapper) */
<T> T getMapper(Class<T> type);
/** 提交事务 */
void commit();
/** 回滚事务 */
void rollback();
/** 关闭 */
@Override
void close();
}
为什么 SqlSession 也是抽象工厂?
3.2 MyBatis DefaultSqlSessionFactory —— 默认实现
源码位置:
- MyBatis 3.5.x
- src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSessionFactory.java
核心源码(简化版):
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
/**
* ✅ 抽象工厂方法:从数据源创建 SqlSession
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 1️⃣ 获取 Environment
final Environment environment = configuration.getEnvironment();
// 2️⃣ 创建事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 3️⃣ 创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 4️⃣ 创建执行器
final Executor executor = configuration.newExecutor(tx, execType);
// 5️⃣ 抽象工厂方法:创建 SqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
// …
} finally {
ErrorContext.instance().reset();
}
}
}
抽象工厂的"产品族"创建过程:
DefaultSqlSessionFactory.openSession()
↓
1. Environment (环境)
↓
2. TransactionFactory (事务工厂)
↓
3. Transaction (事务)
↓
4. Executor (执行器)
↓
5. DefaultSqlSession (会话)
↓
6. SqlSession 内部还能再创建:
├── Mapper (动态代理)
├── StatementHandler
└── ResultSetHandler
3.3 MyBatis-Spring SqlSessionFactoryBean —— FactoryBean 包装抽象工厂
源码位置:
- MyBatis-Spring 3.0.x
- src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java
核心源码:
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ContextRefreshedEvent> {
private Resource configLocation;
private Configuration configuration;
/**
* ✅ FactoryBean 的 getObject() 返回 SqlSessionFactory
*/
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.configuration == null) {
this.configuration = new Configuration();
}
return buildSqlSessionFactory();
}
/**
* ✅ 抽象工厂方法:build 出 SqlSessionFactory
*/
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
// … 解析 XML + 构建 Configuration
return new DefaultSqlSessionFactory(this.configuration);
}
@Override
public Class<?> getObjectType() {
return SqlSessionFactory.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
双层抽象:
调用方
↓ @Autowired
SqlSessionFactory (注入的就是这里)
↓ 由 SqlSessionFactoryBean.getObject() 返回
DefaultSqlSessionFactory (真正的实现)
↓ openSession()
SqlSession (会话)
为什么用 FactoryBean 包装?
3.4 Spring EntityManagerFactory —— JPA 抽象工厂
源码位置:
- Spring Data JPA / Hibernate
- org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
核心源码(简化):
public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManagerFactoryBean {
/**
* ✅ 抽象工厂方法:创建 EntityManagerFactory
*/
@Override
protected EntityManagerFactory createEntityManagerFactory(PersistenceUnitInfo puInfo) {
// 委托给 JpaVendorAdapter 创建
EntityManagerFactory emf = getJpaVendorAdapter().createEntityManagerFactory(
puInfo.getPersistenceUnitName(), ...);
return emf;
}
@Override
public Class<?> getObjectType() {
return EntityManagerFactory.class;
}
}
产品族:
EntityManagerFactory (抽象工厂)
↓ createEntityManager()
EntityManager (会话)
↓
├── Query (查询)
├── TypedQuery (类型化查询)
└── Transaction (事务)
和 MyBatis 的对比:
| 抽象工厂 | SqlSessionFactory | EntityManagerFactory |
| 会话 | SqlSession | EntityManager |
| 查询 | selectList() | createQuery() |
| 事务管理 | commit() / rollback() | getTransaction().begin() |
| 生命周期 | 短(每次操作) | 长(事务范围) |
3.5 中间件抽象工厂对比表
| MyBatis | SqlSessionFactory | SqlSession/Executor/StatementHandler | 5+ |
| MyBatis-Spring | SqlSessionFactoryBean | 同上 | 1(getObject) |
| JPA | EntityManagerFactory | EntityManager/Query/Transaction | 1 |
| Hibernate | SessionFactory | Session/Query/Transaction | 1 |
| Druid | DruidDataSource | Connection/Statement | 1(getConnection) |
规律:
中间件的抽象工厂倾向于"单方法 + 配置驱动"——抽象工厂方法少(通常 1 个),但通过配置和 SPI 支持多实现。这是企业级抽象工厂的典型风格。
四、为什么框架作者这么设计?
读源码看的是"实现",悟的是"设计哲学"。
4.1 框架作者选抽象工厂的 4 个核心考虑
| ① | 一组 API 必须配套 | JDBC Connection + Statement + ResultSet |
| ② | 底层可替换 | 换 MySQL / Oracle 驱动不改调用方 |
| ③ | 生命周期嵌套 | Connection → Statement → ResultSet |
| ④ | 多套实现并存 | DocumentBuilderFactoryImpl / Saxon / Xalan |
4.2 抽象工厂 vs 工厂方法:源码里怎么选?
| 单一产品 | 工厂方法 | LoggerFactory.getLogger() |
| 配套 API 必须一起换 | 抽象工厂 | DocumentBuilderFactory |
| 嵌套生命周期 | 抽象工厂 | Connection / SqlSession |
| 配置驱动 + 多实现 | 抽象工厂 | SqlSessionFactory |
4.3 框架作者不选抽象工厂的 3 种情况
| ① | 产品之间没"配套" | LoggerFactory.getLogger()(用工厂方法) |
| ② | 经常加新组件 | Spring BeanFactory(用工厂方法 + 配置) |
| ③ | 生命周期独立 | 各自独立的工具类(用工厂方法) |
4.4 抽象工厂在企业应用中的 3 种典型用法
用法 1:跨数据库抽象
Connection / Statement / ResultSet
→ 换数据库驱动不改业务代码
用法 2:跨协议抽象
HTTP / gRPC / WebSocket
→ 换协议不改业务代码
用法 3:多端 UI 抽象
PC / H5 / 小程序
→ 换端不改业务代码
五、借鉴到我们项目:3 个"抄作业"实践
5.1 实践 1:跨数据库访问抽象工厂
场景:大白商城要支持 MySQL / OceanBase / PolarDB 三个数据库。
完整实现:
/**
* 借鉴 JDBC Connection:数据库访问抽象工厂
*/
public interface DatabaseSession extends AutoCloseable {
/** 执行 SQL */
int executeUpdate(String sql);
/** 查询(返回单行) */
Map<String, Object> queryForMap(String sql, Object... params);
/** 查询(返回多行) */
List<Map<String, Object>> queryForList(String sql, Object... params);
/** 开启事务 */
void beginTransaction();
/** 提交事务 */
void commit();
/** 回滚事务 */
void rollback();
@Override
void close();
}
/**
* 抽象工厂接口
*/
public interface DatabaseSessionFactory {
/**
* ✅ 抽象工厂方法:创建数据库会话
*/
DatabaseSession openSession();
/**
* 关闭工厂
*/
void close();
}
/**
* MySQL 实现
*/
@Component
public class MysqlSessionFactory implements DatabaseSessionFactory {
private final HikariDataSource dataSource;
public MysqlSessionFactory(HikariDataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public DatabaseSession openSession() {
try {
Connection conn = dataSource.getConnection();
return new MysqlSession(conn); // MySQL 特定的连接包装
} catch (SQLException e) {
throw new RuntimeException("打开 MySQL 会话失败", e);
}
}
@Override
public void close() {
dataSource.close();
}
}
调用方:
@Service
public class OrderService {
@Autowired
private DatabaseSessionFactory sessionFactory;
public List<Order> getOrders(Long userId) {
try (DatabaseSession session = sessionFactory.openSession()) {
String sql = "SELECT * FROM orders WHERE user_id = ?";
return session.queryForList(sql, userId)
.stream()
.map(this::mapToOrder)
.collect(Collectors.toList());
}
}
}
收益:
| 数据库可换 | 改配置即可从 MySQL 换到 OceanBase |
| 调用方无感知 | 业务代码不变 |
| 事务管理统一 | 抽象出 begin/commit/rollback |
5.2 实践 2:跨协议通信抽象工厂
场景:大白商城要支持 HTTP / gRPC / WebSocket 三种通信方式。
完整实现:
/**
* 借鉴 MyBatis SqlSession:通信协议抽象工厂
*/
public interface ProtocolSession extends AutoCloseable {
/** 发送请求并等待响应 */
<T> T request(String endpoint, Object payload, Class<T> responseType);
/** 发送请求(不等待响应) */
void send(String endpoint, Object payload);
/** 监听(WebSocket 用) */
void onMessage(String topic, MessageHandler handler);
@Override
void close();
}
public interface ProtocolSessionFactory {
/**
* ✅ 抽象工厂方法:创建协议会话
*/
ProtocolSession openSession();
}
/**
* HTTP 实现
*/
@Component
public class HttpProtocolSessionFactory implements ProtocolSessionFactory {
private final HttpClient httpClient;
public HttpProtocolSessionFactory() {
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
}
@Override
public ProtocolSession openSession() {
return new HttpProtocolSession(httpClient);
}
}
/**
* gRPC 实现
*/
@Component
public class GrpcProtocolSessionFactory implements ProtocolSessionFactory {
private final ManagedChannel channel;
public GrpcProtocolSessionFactory() {
this.channel = ManagedChannelBuilder
.forAddress("localhost", 50051)
.usePlaintext()
.build();
}
@Override
public ProtocolSession openSession() {
return new GrpcProtocolSession(channel);
}
}
收益:
| 协议可换 | 改配置从 HTTP 切到 gRPC |
| 业务代码统一 | 同 request() API |
| 多协议并存 | 同一项目支持多协议 |
5.3 实践 3:业务上下文抽象工厂
场景:大白商城每个业务操作都需要一组上下文(用户信息、租户、链路追踪)。
完整实现:
/**
* 借鉴 JDBC Connection:业务上下文抽象工厂
*/
public interface BusinessContext extends AutoCloseable {
/** 获取当前用户 */
String getCurrentUserId();
/** 获取当前租户 */
String getCurrentTenantId();
/** 获取链路追踪 ID */
String getTraceId();
/** 设置属性 */
void setAttribute(String key, Object value);
/** 获取属性 */
Object getAttribute(String key);
@Override
void close();
}
public interface BusinessContextFactory {
/**
* ✅ 抽象工厂方法:创建业务上下文
*/
BusinessContext createContext();
}
/**
* Web 上下文实现(基于 ThreadLocal)
*/
@Component
public class WebBusinessContextFactory implements BusinessContextFactory {
@Override
public BusinessContext createContext() {
return new ThreadLocalBusinessContext(); // 上一篇单例讲过的 ThreadLocal
}
}
调用方:
@Service
public class OrderService {
@Autowired
private BusinessContextFactory contextFactory;
public void createOrder(OrderRequest request) {
try (BusinessContext ctx = contextFactory.createContext()) {
log.info("创建订单, userId={}, tenantId={}, traceId={}",
ctx.getCurrentUserId(),
ctx.getCurrentTenantId(),
ctx.getTraceId());
// 业务逻辑…
}
}
}
收益:
| 上下文统一 | 业务代码不需要关心 ThreadLocal / MDC |
| 可换实现 | Web 上下文 / 定时任务上下文 / RPC 上下文可切换 |
| 自动清理 | try-with-resources 保证清理 |
六、本篇小结 + 下一模式预告
6.1 本篇小结(5 个核心要点)
6.2 一句话总结
抽象工厂在源码里不是"教科书式"的族,而是"按规范凑齐一组相关 API"。JDK 用来支持多实现,MyBatis 用来组织 SQL 会话,Spring 用来支持配置化。用对场景 = 救命,用错 = 灾难。
6.3 知识脑图
抽象工厂源码剖析
├── JDK
│ ├── DocumentBuilderFactory(XML 解析产品族)
│ ├── Connection(JDBC 嵌套抽象工厂)
│ └── TransformerFactory(XSLT 转换产品族)
├── MyBatis
│ ├── SqlSessionFactory(抽象工厂接口)
│ ├── DefaultSqlSessionFactory(默认实现)
│ └── SqlSessionFactoryBean(FactoryBean 包装)
├── Spring
│ ├── LocalContainerEntityManagerFactoryBean(JPA)
│ └── FactoryBean 通用模式
└── 借鉴价值
├── 跨数据库访问(DatabaseSession)
├── 跨协议通信(ProtocolSession)
└── 业务上下文(BusinessContext)
6.4 下一模式预告
第 08 篇【建造者模式 – 业务实战篇】:大白商城复杂订单对象的"装配工"
下一篇我们会实战建造者模式,回答 4 个问题:
并附完整的订单构建代码(订单构造器 + Lombok @Builder 对比 + 单元测试),可直接复制到 IDEA 跑。
觉得对您有帮助,麻烦点点关注啦,您的关注是我创作的最大动力~ 🎯

