从“搬砖”到“造航母”:Java 工程实践中的 5 大设计模式深度剖析

举报
江南清风起 发表于 2025/07/12 17:15:13 2025/07/12
【摘要】 从“搬砖”到“造航母”:Java 工程实践中的 5 大设计模式深度剖析作者:Kimi | 2025-07-12 | 阅读时间:≈ 25 min 为什么 2025 年还要聊设计模式?过去 20 年,Java 生态从单机系统演进到云原生、Serverless;从单线程 synchronized 到 Loom 虚拟线程;从 Spring 1.x 到 Spring Boot 3.x。设计模式没有过...

从“搬砖”到“造航母”: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 让查询可读可测。

image.png

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。