📌 PDF :AI人工智能 --- AI Agent智能体实战项目
大白话说Java设计模式-23-桥接模式(源码剖析篇):JDK / AWT / Logging 中的桥接实现
📌 一句话本质:桥接在源码里不是"两个维度"的形象化,而是"抽象和实现彻底分离"的工业级架构。
🏷️ 标签:桥接模式 / JDBC DriverManager / AWT Component Peer / Java Logging 🎯 适合:中高级后端 / 想读懂主流框架源码的工程师
目录
- 一、为什么读桥接源码?一张地图先看清
- [二、JDK 源码剖析:JDBC DriverManager](#二、JDK 源码剖析:JDBC DriverManager)
- [三、JDK 源码剖析:AWT Component / Peer](#三、JDK 源码剖析:AWT Component / Peer)
- [四、JDK 源码剖析:Java Logging Logger / Handler](#四、JDK 源码剖析:Java Logging Logger / Handler)
- 五、为什么框架作者这么设计?
- [六、借鉴到我们项目:3 个"抄作业"实践](#六、借鉴到我们项目:3 个"抄作业"实践)
- [七、本篇小结 + 下一模式预告](#七、本篇小结 + 下一模式预告)
一、为什么读桥接源码?一张地图先看清
桥接模式在企业级框架里出现得极其关键------它解决了"多维度变化"的经典难题。
源码地图:
| 类别 | 框架 / 中间件 | 抽象 | 实现 |
|---|---|---|---|
| JDK | JDBC | DriverManager |
Driver |
| JDK | AWT | Component |
ComponentPeer |
| JDK | Swing | JComponent |
LookAndFeel / UIResource |
| JDK | Java Logging | Logger |
Handler |
| JDK | JNDI | Context |
InitialContextFactory |
| JDK | Preferences | Preferences |
PreferencesFactory |
| Spring | Spring AOP | AopProxy |
AopProxyFactory |
| MyBatis | MyBatis | SqlSessionFactory |
SqlSessionFactoryBuilder |
| Hibernate | Hibernate | SessionFactory |
ConnectionProvider |
我们按"JDBC → AWT → Logging"的顺序来剖析。
二、JDK 源码剖析:JDBC DriverManager
2.1 JDBC 的桥接结构
JDBC 是桥接模式的"教科书级"实现:
DriverManager(抽象 / 客户端 API)
├── Driver driver ← 桥(持有驱动引用)
├── MySQL Driver
├── PostgreSQL Driver
├── Oracle Driver
└── SQL Server Driver
关键点:
- 抽象 :
DriverManager.getConnection()(客户端 API) - 实现 :
Driver接口(具体驱动) - 桥 :
DriverManager持有Driver引用
2.2 DriverManager 核心源码
源码位置:
- OpenJDK 17
src/java.sql/share/classes/java/sql/DriverManager.java
核心源码(简化版):
java
/**
* ✅ 抽象:DriverManager
* <p>
* 管理一组 JDBC 驱动,提供统一的连接获取 API
*/
public class DriverManager {
/**
* ✅ 已注册的驱动列表(实现类)
*/
private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
/**
* ✅ 桥:客户端只调 getConnection()
*/
@CallerSensitive
public static Connection getConnection(String url, String user, String password) throws SQLException {
// 1️⃣ 找到合适的 Driver
Driver driver = getDriver(url);
// 2️⃣ 调 Driver 的 connect
return driver.connect(url, new Properties());
}
/**
* ✅ 找到合适的 Driver
*/
private static Driver getDriver(String url) throws SQLException {
for (DriverInfo aDriver : registeredDrivers) {
// 如果是当前调用方的 ClassLoader,跳过
if (isDriverAllowed(aDriver.driver, Reflection.getCallerClass())) {
try {
// ✅ 调用 Driver.acceptsURL()
if (aDriver.driver.acceptsURL(url)) {
return aDriver.driver;
}
} catch (SQLException e) {
// ...
}
}
}
throw new SQLException("No suitable driver found for " + url);
}
/**
* ✅ 注册驱动
*/
public static synchronized void registerDriver(Driver driver) throws SQLException {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
}
}
关键点:
| 关键点 | 解释 |
|---|---|
registeredDrivers |
实现类列表(多种数据库驱动) |
getConnection() |
客户端统一 API |
Driver.acceptsURL() |
桥(选择实现) |
2.3 Driver 接口(实现)
核心源码:
java
/**
* ✅ 实现接口:Driver
* <p>
* 每个数据库驱动实现这个接口
*/
public interface Driver {
/**
* 接受 URL(判断是否能处理)
*/
boolean acceptsURL(String url) throws SQLException;
/**
* 连接数据库
*/
Connection connect(String url, Properties info) throws SQLException;
/**
* 获取属性
*/
DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException;
/**
* 主版本
*/
int getMajorVersion();
/**
* 次版本
*/
int getMinorVersion();
}
2.4 MySQL 驱动实现
核心源码(简化版):
java
/**
* ✅ 具体实现:MySQL 驱动
*/
public class com.mysql.cj.jdbc.Driver implements Driver {
static {
try {
// ✅ 自动注册
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
} catch (SQLException e) {
throw new RuntimeException("Can't register MySQL driver!");
}
}
@Override
public boolean acceptsURL(String url) throws SQLException {
return url != null && url.startsWith("jdbc:mysql:");
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
// 实际建立 MySQL 连接
return new com.mysql.cj.jdbc.ConnectionImpl(url, info);
}
}
2.5 完整 JDBC 桥接调用
应用代码
↓
DriverManager.getConnection("jdbc:mysql://localhost:3306/db")
↓ 桥:找到 MySQL Driver
com.mysql.cj.jdbc.Driver.acceptsURL() → true
↓ 桥:调 connect
com.mysql.cj.jdbc.Driver.connect()
↓
Connection(MySQL 连接)
关键点:
| 关键点 | 解释 |
|---|---|
| 客户端只调 DriverManager | 不知道底层是 MySQL 还是 Oracle |
| 切换数据库 | 改 URL,不改代码 |
| 加新数据库 | 加新 Driver,DriverManager 不动 |
2.6 JDBC 桥接的"作者选择"
| 设计点 | 选择 | 原因 |
|---|---|---|
| Driver 接口 | 统一规范 | 数据库厂商实现 |
| DriverManager 注册 | 桥接点 | 运行时发现 |
| SPI 机制 | ServiceLoader | 自动加载 |
| acceptsURL | 选择实现 | 灵活 |
三、JDK 源码剖析:AWT Component / Peer
3.1 AWT 的桥接结构
AWT 是经典的桥接模式应用:
Component(抽象:界面)
├── ComponentPeer peer ← 桥(持有平台相关实现)
├── Button → ButtonPeer(Windows / Linux / Mac 不同实现)
├── TextField → TextFieldPeer
└── Container → ContainerPeer
关键点:
- 抽象 :
Component(跨平台 Java 接口) - 实现 :
ComponentPeer(平台相关原生组件) - 桥 :
Component持有ComponentPeer
3.2 Component 抽象类
源码位置:
- OpenJDK 17
src/java.desktop/share/classes/java/awt/Component.java
核心源码(简化版):
java
/**
* ✅ 抽象:AWT Component
* <p>
* 跨平台的 Java 组件接口
*/
public abstract class Component implements ImageObserver, MenuContainer, Serializable {
/**
* ✅ 桥:持有平台相关的 Peer
*/
transient volatile ComponentPeer peer;
/**
* 通用操作
*/
public void setLocation(int x, int y) {
// 1. Java 层处理
this.x = x;
this.y = y;
// 2. ✅ 桥:调 peer
if (peer != null) {
peer.setLocation(x, y);
}
}
public void setSize(int width, int height) {
this.width = width;
this.height = height;
// ✅ 桥:调 peer
if (peer != null) {
peer.setSize(width, height);
}
}
}
关键点:
| 关键点 | 解释 |
|---|---|
peer |
桥(持有平台相关实现) |
| Java 层 | 跨平台(位置/大小) |
| Peer 层 | 平台相关(Windows / Linux / Mac) |
3.3 ComponentPeer 接口(实现)
核心源码:
java
/**
* ✅ 实现接口:ComponentPeer
* <p>
* 平台相关的原生组件接口
*/
public interface ComponentPeer {
void setLocation(int x, int y);
void setSize(int width, int height);
void setVisible(boolean v);
void dispose();
// ... 其他平台相关方法
}
3.4 平台相关实现
Windows 实现(WComponentPeer):
java
/**
* ✅ 具体实现:Windows 平台
*/
public class WComponentPeer implements ComponentPeer {
@Override
public void setLocation(int x, int y) {
// Windows API:SetWindowPos
// 实际调用 Windows 底层
}
}
/**
* Linux 实现(`XComponentPeer`)
*/
public class XComponentPeer implements ComponentPeer {
@Override
public void setLocation(int x, int y) {
// X11 API:XMoveWindow
// 实际调用 X11 底层
}
}
3.5 AWT 桥接的"作者选择"
| 设计点 | 选择 | 原因 |
|---|---|---|
| Component 抽象 | 跨平台 Java 接口 | 一套代码 |
| ComponentPeer 实现 | 平台相关原生 | 性能 |
| Peer 桥 | 持有平台实现 | 灵活切换 |
| Toolkit | 创建 Peer | 工厂 |
四、JDK 源码剖析:Java Logging Logger / Handler
4.1 Java Logging 的桥接结构
Java Logging 也是桥接模式:
Logger(抽象:日志记录器)
├── Handler handler ← 桥(持有日志输出器)
├── ConsoleHandler
├── FileHandler
└── SocketHandler
关键点:
- 抽象 :
Logger(日志记录 API) - 实现 :
Handler接口(具体输出位置) - 桥 :
Logger持有Handler列表
4.2 Logger 抽象类
源码位置:
- OpenJDK 17
src/java.logging/share/classes/java/util/logging/Logger.java
核心源码(简化版):
java
/**
* ✅ 抽象:Logger
* <p>
* 提供日志记录 API
*/
public class Logger {
/**
* ✅ 桥:日志处理器列表
*/
private final CopyOnWriteArrayList<Handler> handlers = new CopyOnWriteArrayList<>();
/**
* 记录日志
*/
public void log(LogRecord record) {
// 1️⃣ 过滤
if (!isLoggable(record.getLevel())) {
return;
}
// 2️⃣ 调过滤器
// ...
// 3️⃣ ✅ 桥:调所有 Handler
for (Handler handler : handlers) {
handler.publish(record);
}
}
/**
* 添加 Handler
*/
public void addHandler(Handler handler) {
handlers.add(handler);
}
}
4.3 Handler 接口(实现)
核心源码:
java
/**
* ✅ 实现接口:Handler
*/
public abstract class Handler {
/**
* 输出日志
*/
public abstract void publish(LogRecord record);
/**
* 刷新
*/
public abstract void flush();
/**
* 关闭
*/
public abstract void close();
}
4.4 具体实现
java
/**
* ✅ 具体实现:控制台 Handler
*/
public class ConsoleHandler extends Handler {
@Override
public void publish(LogRecord record) {
System.err.println(record.getMessage());
}
@Override
public void flush() { System.err.flush(); }
@Override
public void close() { }
}
/**
* ✅ 具体实现:文件 Handler
*/
public class FileHandler extends Handler {
private final String filePath;
public FileHandler(String filePath) {
this.filePath = filePath;
}
@Override
public void publish(LogRecord record) {
// 写入文件
}
}
4.5 Logging 桥接的"作者选择"
| 设计点 | 选择 | 原因 |
|---|---|---|
| Logger 抽象 | 客户端 API | 统一记录 |
| Handler 实现 | 多种输出 | 灵活 |
| 多 Handler | 列表 | 多个输出 |
| Filter / Formatter | 装饰 | 增强 |
五、为什么框架作者这么设计?
读源码看的是"实现",悟的是"设计哲学"。
5.1 框架作者选桥接的 4 个核心考虑
| 序号 | 考虑 | 举例 |
|---|---|---|
| ① | 两个独立维度变化 | JDBC 类型 × 数据库 |
| ② | 避免多继承 | AWT 界面 × 平台 |
| ③ | 运行时动态组合 | Logging Logger × Handler |
| ④ | 保护抽象不变 | 抽象稳定,实现多变 |
5.2 桥接 vs 继承:源码里怎么选?
| 场景 | 选哪个 | 原因 |
|---|---|---|
| 多维度变化 | 桥接 | 组合 |
| 单维度 | 继承 | 简单 |
| 运行时切换 | 桥接 | 灵活 |
| 编译期确定 | 继承 | 稳定 |
5.3 框架作者不选桥接的 3 种情况
| 序号 | 场景 | 原因 |
|---|---|---|
| ① | 只有一个维度 | 桥接过度设计 |
| ② | 维度不变化 | 用枚举 |
| ③ | 业务简单 | 普通类 |
5.4 桥接在企业级应用的 3 种典型用法
用法 1:JDBC 数据库切换
DriverManager × Driver
→ 切换数据库不改业务
用法 2:AWT 跨平台
Component × ComponentPeer
→ 一套代码跑遍所有平台
用法 3:日志多输出
Logger × Handler
→ 同时输出控制台和文件
六、借鉴到我们项目:3 个"抄作业"实践
6.1 实践 1:多支付渠道桥接
场景:大白商城多支付渠道(一次性/分期 × 支付宝/微信)。
完整实现:
java
/**
* 借鉴 JDBC DriverManager:支付桥接
*/
public interface PaymentDriver {
boolean accepts(String channel);
void pay(BigDecimal amount);
}
/**
* 支付宝驱动
*/
public class AlipayDriver implements PaymentDriver {
@Override
public boolean accepts(String channel) {
return "alipay".equals(channel);
}
@Override
public void pay(BigDecimal amount) {
System.out.println("支付宝支付: " + amount);
}
}
/**
* 微信驱动
*/
public class WechatDriver implements PaymentDriver {
@Override
public boolean accepts(String channel) {
return "wechat".equals(channel);
}
@Override
public void pay(BigDecimal amount) {
System.out.println("微信支付: " + amount);
}
}
/**
* 支付客户端(桥)
*/
public class PaymentClient {
private final List<PaymentDriver> drivers = new ArrayList<>();
public PaymentClient(List<PaymentDriver> driverList) {
this.drivers.addAll(driverList);
}
public void pay(String channel, BigDecimal amount) {
// 找驱动
PaymentDriver driver = drivers.stream()
.filter(d -> d.accepts(channel))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("不支持: " + channel));
// 桥:调驱动
driver.pay(amount);
}
}
// 使用
PaymentClient client = new PaymentClient(List.of(
new AlipayDriver(),
new WechatDriver()
));
client.pay("alipay", new BigDecimal("100"));
6.2 实践 2:多输出日志桥接
场景:大白商城日志同时输出控制台、文件、远程。
完整实现:
java
/**
* 借鉴 Java Logging:日志桥接
*/
public interface LogHandler {
void publish(String message);
}
/**
* 控制台输出
*/
public class ConsoleHandler implements LogHandler {
@Override
public void publish(String message) {
System.out.println("[CONSOLE] " + message);
}
}
/**
* 文件输出
*/
public class FileHandler implements LogHandler {
private final String filePath;
public FileHandler(String filePath) {
this.filePath = filePath;
}
@Override
public void publish(String message) {
// 写入文件
}
}
/**
* 远程输出
*/
public class RemoteHandler implements LogHandler {
@Override
public void publish(String message) {
// HTTP 上报
}
}
/**
* 日志器(桥)
*/
public class Logger {
private final List<LogHandler> handlers = new ArrayList<>();
public void addHandler(LogHandler handler) {
handlers.add(handler);
}
public void log(String message) {
// 桥:调所有 handler
for (LogHandler handler : handlers) {
handler.publish(message);
}
}
}
// 使用
Logger logger = new Logger();
logger.addHandler(new ConsoleHandler());
logger.addHandler(new FileHandler("/var/log/app.log"));
logger.addHandler(new RemoteHandler());
logger.log("Hello");
// 同时输出:控制台 + 文件 + 远程
6.3 实践 3:跨平台 UI 桥接
场景:大白商城客户端需要同时支持 Web / 移动端 / 桌面。
完整实现:
java
/**
* 借鉴 AWT:跨平台 UI 桥接
*/
public interface UIPeer {
void render(String content);
void onClick(String event);
}
/**
* Web 实现
*/
public class WebUIPeer implements UIPeer {
@Override
public void render(String content) {
// 渲染 HTML
}
@Override
public void onClick(String event) {
// 浏览器事件
}
}
/**
* 移动端实现
*/
public class MobileUIPeer implements UIPeer {
@Override
public void render(String content) {
// 渲染原生界面
}
@Override
public void onClick(String event) {
// 移动端事件
}
}
/**
* UI 组件(桥)
*/
public class UIComponent {
private UIPeer peer;
public void setPeer(UIPeer peer) {
this.peer = peer;
}
public void show(String content) {
peer.render(content);
}
}
// 使用
UIComponent component = new UIComponent();
component.setPeer(new WebUIPeer());
component.show("订单详情");
收益:
| 优化点 | 效果 |
|---|---|
| 多支付统一 | 任意组合支付 |
| 多输出日志 | 灵活配置 |
| 跨平台 UI | 业务代码不变 |
七、本篇小结 + 下一模式预告
7.1 本篇小结(5 个核心要点)
- JDBC DriverManager :经典桥接,抽象和数据库驱动独立。
- AWT Component / Peer :跨平台桥接,Java 接口和原生实现分离。
- Java Logging Logger / Handler :日志桥接,Logger 和输出位置独立。
- 设计哲学 :抽象稳定 + 实现多变 + 桥作为连接点。
- 借鉴价值 :支付/日志/UI,任何多维度组合都用桥接。
7.2 一句话总结
桥接在源码里不是"两个维度"的形象化,而是"抽象和实现彻底分离"的工业级架构 。JDBC 让数据库可换,AWT 让平台可换,Logging 让输出可换。学桥接,先学 JDBC。
7.3 知识脑图
桥接源码剖析
├── JDBC
│ ├── DriverManager(抽象)
│ ├── Driver(实现接口)
│ └── MySQLDriver(具体实现)
├── AWT
│ ├── Component(抽象)
│ ├── ComponentPeer(实现接口)
│ ├── WComponentPeer(Windows)
│ └── XComponentPeer(Linux)
├── Java Logging
│ ├── Logger(抽象)
│ ├── Handler(实现接口)
│ ├── ConsoleHandler
│ └── FileHandler
└── 借鉴价值
├── 多支付渠道桥接
├── 多输出日志桥接
└── 跨平台 UI 桥接
7.4 下一模式预告
第 24 篇【享元模式 - 业务实战篇】:大白商城商品 SKU 共享的"内存减肥药"
下一篇我们会实战享元模式,回答 4 个问题:
- 商品标签 / 库存 SKU 怎么共享减少内存?
- 享元 vs 单例 / 缓存 / 对象池怎么区分?
- Java 的
Integer.valueOf怎么用享元? - 享元 vs 原型怎么选?
并附完整的商品享元实战代码(含内部状态 + 外部状态 + 单元测试),可直接复制到 IDEA 跑。
觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯