从“搬砖”到“造航母”:Java 工程实践中的 5 大设计模式深度剖析
从“搬砖”到“造航母”:Java 工程实践中的 5 大设计模式深度剖析
作者:Kimi | 2025-07-12 | 阅读时间:≈ 25 min
为什么 2025 年还要聊设计模式?
过去 20 年,Java 生态从单机系统演进到云原生、Serverless;从单线程 synchronized
到 Loom 虚拟线程;从 Spring 1.x 到 Spring Boot 3.x。
设计模式没有过时,反而在更高维度被复用:
- 响应式编程框架(Reactor、RxJava)大量采用 观察者 + 责任链;
- Spring Cloud Gateway 路由逻辑用 策略 做动态过滤器;
- 分布式链路追踪用 装饰者 无侵入地增强日志。
本文挑选 5 个在真实 Java 工程中“肉眼可见”的模式,结合 Spring Boot 3.2、JDK 21、Gradle Kotlin DSL,给出可运行的最小完整示例(MVP),并讨论其在云原生时代的演进。
1. 策略模式:把“变化”关进笼子里
1.1 场景:跨境支付路由
某跨境电商平台需根据用户 IP、币种、风控评级动态选择支付通道(Stripe、Adyen、支付宝)。新增通道不应改动老代码。
1.2 经典实现 VS Spring 注入
1.2.1 支付策略抽象
public sealed interface PaymentStrategy permits StripeStrategy, AdyenStrategy, AlipayStrategy {
boolean accept(PaymentContext ctx);
PaymentResult pay(PaymentRequest req);
}
1.2.2 具体策略实现(节选 Stripe)
@Component
@Order(1)
public final class StripeStrategy implements PaymentStrategy {
@Override
public boolean accept(PaymentContext ctx) {
return "USD".equals(ctx.currency()) && ctx.riskScore() < 30;
}
@Override
public PaymentResult pay(PaymentRequest req) {
// 调用 Stripe SDK
return new PaymentResult(UUID.randomUUID().toString(), "STRIPE");
}
}
1.2.3 策略路由器(零 if-else)
@Component
public class PaymentRouter {
private final List<PaymentStrategy> strategies;
public PaymentRouter(List<PaymentStrategy> strategies) {
this.strategies = strategies.stream()
.sorted(OrderComparator.INSTANCE) // 利用 @Order
.toList();
}
public PaymentStrategy route(PaymentContext ctx) {
return strategies.stream()
.filter(s -> s.accept(ctx))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No channel available"));
}
}
1.3 云原生演进
- 配置中心(Spring Cloud Config)动态推送通道权重,策略 Bean 通过
@RefreshScope
动态生效。 - GraalVM Native Image 下,
sealed interface
+switch
表达式编译为高效tableswitch
,无反射损耗。
2. 装饰者模式:给日志加一层“分布式盔甲”
2.1 场景:零侵入注入 TraceId
传统做法用 MDC.put()
污染业务线程;我们希望在 不改动任何业务代码 的前提下:
- HTTP 入口自动注入 TraceId;
- 所有日志自动带上该 TraceId;
- 支持虚拟线程(Project Loom)。
2.2 装饰 Logger
2.2.1 抽象装饰器
public abstract class LoggerDecorator implements Logger {
protected final Logger delegate;
protected LoggerDecorator(Logger delegate) { this.delegate = delegate; }
@Override
public void info(String format, Object... arguments) {
delegate.info(addTraceId(format), arguments);
}
private String addTraceId(String msg) {
return "[" + TraceContext.traceId() + "] " + msg;
}
}
2.2.2 SLF4J 绑定
public final class TraceLogger extends LoggerDecorator {
public TraceLogger(Logger delegate) { super(delegate); }
}
// 通过 LoggerFactory.getLogger() 拦截
public final class TraceLoggerFactory {
public static Logger getLogger(Class<?> clazz) {
Logger raw = LoggerFactory.getLogger(clazz);
return new TraceLogger(raw);
}
}
2.3 虚拟线程兼容
在 Loom 中,每个虚拟线程携带自己的 TraceContext
(基于 ScopedValue
):
private static final ScopedValue<String> TRACE_HOLDER = ScopedValue.newInstance();
装饰器内 TraceContext.traceId()
实际读取 TRACE_HOLDER.get()
,既 无 ThreadLocal 泄漏,又 支持上百万并发。
3. 责任链模式:Spring Security Filter 的教科书实现
3.1 场景:多因素认证(MFA)
系统支持短信 OTP、TOTP、FIDO2 三种认证方式,顺序可配置。
3.2 链式过滤器
3.2.1 抽象认证过滤器
public abstract class MfaFilter {
private MfaFilter next;
public MfaFilter linkWith(MfaFilter next) {
this.next = next;
return next;
}
public final void handle(AuthContext ctx) {
if (doFilter(ctx)) return; // 当前过滤器已处理
if (next != null) next.handle(ctx);
}
protected abstract boolean doFilter(AuthContext ctx);
}
3.2.2 具体实现(FIDO2)
@Component
public final class Fido2Filter extends MfaFilter {
@Override
protected boolean doFilter(AuthContext ctx) {
if (!ctx.device().supportsFido2()) return false;
// 调用 WebAuthn4J
return true;
}
}
3.3 与 Spring Security 集成
通过 SecurityFilterChain
把责任链注册为 最后一个 Filter:
@Bean
public SecurityFilterChain chain(HttpSecurity http, List<MfaFilter> filters) throws Exception {
MfaFilter head = filters.get(0);
for (int i = 1; i < filters.size(); i++) head.linkWith(filters.get(i));
http.addFilterAfter(new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
AuthContext ctx = new AuthContext(req);
head.handle(ctx);
chain.doFilter(req, res);
}
}, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
4. 观察者模式:事件驱动解耦下单流程
4.1 场景:订单状态变更广播
下单成功后需:
- 发站内信;
- 通知库存扣减;
- 触发风控反欺诈;
- 推送到 WebSocket 客户端。
4.2 Spring ApplicationEvent 实现
4.2.1 定义领域事件
public record OrderCreatedEvent(Long orderId, Long userId, BigDecimal amount) {}
4.2.2 发布事件(同步 → 异步)
@Service
public class OrderService {
private final ApplicationEventPublisher publisher;
@Transactional
public void createOrder(CreateOrderCommand cmd) {
Order order = saveToDB(cmd);
publisher.publishEvent(new OrderCreatedEvent(order.getId(), order.getUserId(), order.getAmount()));
}
}
4.2.3 监听(支持虚拟线程)
@Component
public class InventoryListener {
@Async("virtualTaskExecutor") // 使用虚拟线程池
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent e) {
inventoryService.deduct(e.orderId(), e.amount());
}
}
4.3 云原生扩展
- Kafka Stream 作为事件总线,Spring Cloud Stream 自动桥接本地事件到 Kafka Topic;
- Saga 模式:监听者失败时发布
OrderRollbackEvent
,实现分布式事务补偿。
5. 建造者模式:DSL 构造复杂查询对象
5.1 场景:Elasticsearch 动态查询
用户可按商品类目、品牌、价格区间、库存、地理位置组合查询,字段 20+。
5.2 类型安全 DSL
5.2.1 建造者接口
public sealed interface QueryBuilder permits BoolQueryBuilder {
static BoolQueryBuilder bool() { return new BoolQueryBuilder(); }
}
public final class BoolQueryBuilder {
private final List<Query> must = new ArrayList<>();
private final List<Query> filter = new ArrayList<>();
public BoolQueryBuilder must(Query q) { must.add(q); return this; }
public BoolQueryBuilder filter(Query q) { filter.add(q); return this; }
public Query build() {
return new BoolQuery(must, filter);
}
}
5.2.3 链式调用示例
Query query = QueryBuilder.bool()
.must(term("category", "laptop"))
.filter(range("price").gte(1000).lte(5000))
.filter(geoDistance("location", userLat, userLon, "10km"))
.build();
5.3 与 Spring Data Elasticsearch 集成
通过 QueryBuilder
生成 Query
,直接用于 ElasticsearchRepository
:
SearchHits<Product> hits = repository.search(query, PageRequest.of(0, 20));
结语:模式是“套路”,更是“套路之上的套路”
- 策略 + 配置中心 解决业务多变;
- 装饰者 + ScopedValue 让可观测性无侵入;
- 责任链 + Spring Security 把复杂流程拆成乐高;
- 观察者 + 事件总线 实现微服务松耦合;
- 建造者 + DSL 让查询可读可测。
- 点赞
- 收藏
- 关注作者
评论(0)